aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Wallabag
diff options
context:
space:
mode:
Diffstat (limited to 'src/Wallabag')
-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.php49
-rw-r--r--src/Wallabag/CoreBundle/Controller/ExportController.php2
-rw-r--r--src/Wallabag/CoreBundle/Controller/RssController.php36
-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/Form/Type/SearchEntryType.php29
-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.php44
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php18
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml32
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml38
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml40
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml32
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml38
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml39
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml38
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml38
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml116
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml40
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml37
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml38
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml39
-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.twig16
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig7
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig17
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig12
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig4
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig10
-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.twig17
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig30
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig15
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig3
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig16
-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
87 files changed, 2342 insertions, 248 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..8c13255e 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -13,10 +13,37 @@ 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;
18use Wallabag\CoreBundle\Form\Type\SearchEntryType;
16 19
17class EntryController extends Controller 20class EntryController extends Controller
18{ 21{
19 /** 22 /**
23 * @param Request $request
24 * @param int $page
25 *
26 * @Route("/search/{page}", name="search", defaults={"page" = "1"})
27 *
28 * @return \Symfony\Component\HttpFoundation\Response
29 */
30 public function searchFormAction(Request $request, $page, $currentRoute)
31 {
32 $form = $this->createForm(SearchEntryType::class);
33
34 $form->handleRequest($request);
35
36 if ($form->isValid()) {
37 return $this->showEntries('search', $request, $page);
38 }
39
40 return $this->render('WallabagCoreBundle:Entry:search_form.html.twig', [
41 'form' => $form->createView(),
42 'currentRoute' => $currentRoute,
43 ]);
44 }
45
46 /**
20 * Fetch content and update entry. 47 * Fetch content and update entry.
21 * In case it fails, entry will return to avod loosing the data. 48 * In case it fails, entry will return to avod loosing the data.
22 * 49 *
@@ -81,6 +108,9 @@ class EntryController extends Controller
81 $em->persist($entry); 108 $em->persist($entry);
82 $em->flush(); 109 $em->flush();
83 110
111 // entry saved, dispatch event about it!
112 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
113
84 return $this->redirect($this->generateUrl('homepage')); 114 return $this->redirect($this->generateUrl('homepage'));
85 } 115 }
86 116
@@ -107,6 +137,9 @@ class EntryController extends Controller
107 $em = $this->getDoctrine()->getManager(); 137 $em = $this->getDoctrine()->getManager();
108 $em->persist($entry); 138 $em->persist($entry);
109 $em->flush(); 139 $em->flush();
140
141 // entry saved, dispatch event about it!
142 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
110 } 143 }
111 144
112 return $this->redirect($this->generateUrl('homepage')); 145 return $this->redirect($this->generateUrl('homepage'));
@@ -236,8 +269,14 @@ class EntryController extends Controller
236 private function showEntries($type, Request $request, $page) 269 private function showEntries($type, Request $request, $page)
237 { 270 {
238 $repository = $this->get('wallabag_core.entry_repository'); 271 $repository = $this->get('wallabag_core.entry_repository');
272 $searchTerm = (isset($request->get('search_entry')['term']) ? $request->get('search_entry')['term'] : '');
273 $currentRoute = (!is_null($request->get('currentRoute')) ? $request->get('currentRoute') : '');
239 274
240 switch ($type) { 275 switch ($type) {
276 case 'search':
277 $qb = $repository->getBuilderForSearchByUser($this->getUser()->getId(), $searchTerm, $currentRoute);
278
279 break;
241 case 'untagged': 280 case 'untagged':
242 $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId()); 281 $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId());
243 282
@@ -286,11 +325,11 @@ class EntryController extends Controller
286 } 325 }
287 326
288 return $this->render( 327 return $this->render(
289 'WallabagCoreBundle:Entry:entries.html.twig', 328 'WallabagCoreBundle:Entry:entries.html.twig', [
290 [
291 'form' => $form->createView(), 329 'form' => $form->createView(),
292 'entries' => $entries, 330 'entries' => $entries,
293 'currentPage' => $page, 331 'currentPage' => $page,
332 'searchTerm' => $searchTerm,
294 ] 333 ]
295 ); 334 );
296 } 335 }
@@ -343,6 +382,9 @@ class EntryController extends Controller
343 $em->persist($entry); 382 $em->persist($entry);
344 $em->flush(); 383 $em->flush();
345 384
385 // entry saved, dispatch event about it!
386 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
387
346 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()])); 388 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
347 } 389 }
348 390
@@ -431,6 +473,9 @@ class EntryController extends Controller
431 UrlGeneratorInterface::ABSOLUTE_PATH 473 UrlGeneratorInterface::ABSOLUTE_PATH
432 ); 474 );
433 475
476 // entry deleted, dispatch event about it!
477 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
478
434 $em = $this->getDoctrine()->getManager(); 479 $em = $this->getDoctrine()->getManager();
435 $em->remove($entry); 480 $em->remove($entry);
436 $em->flush(); 481 $em->flush();
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php
index 79653cfe..abc3336a 100644
--- a/src/Wallabag/CoreBundle/Controller/ExportController.php
+++ b/src/Wallabag/CoreBundle/Controller/ExportController.php
@@ -48,7 +48,7 @@ class ExportController extends Controller
48 * 48 *
49 * @Route("/export/{category}.{format}", name="export_entries", requirements={ 49 * @Route("/export/{category}.{format}", name="export_entries", requirements={
50 * "format": "epub|mobi|pdf|json|xml|txt|csv", 50 * "format": "epub|mobi|pdf|json|xml|txt|csv",
51 * "category": "all|unread|starred|archive|tag_entries|untagged" 51 * "category": "all|unread|starred|archive|tag_entries|untagged|search"
52 * }) 52 * })
53 * 53 *
54 * @return \Symfony\Component\HttpFoundation\Response 54 * @return \Symfony\Component\HttpFoundation\Response
diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php
index 38e3b5a0..2290386f 100644
--- a/src/Wallabag/CoreBundle/Controller/RssController.php
+++ b/src/Wallabag/CoreBundle/Controller/RssController.php
@@ -3,12 +3,15 @@
3namespace Wallabag\CoreBundle\Controller; 3namespace Wallabag\CoreBundle\Controller;
4 4
5use Pagerfanta\Adapter\DoctrineORMAdapter; 5use Pagerfanta\Adapter\DoctrineORMAdapter;
6use Pagerfanta\Exception\OutOfRangeCurrentPageException;
6use Pagerfanta\Pagerfanta; 7use Pagerfanta\Pagerfanta;
7use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; 8use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
8use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 9use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
10use Symfony\Component\HttpFoundation\Request;
9use Symfony\Bundle\FrameworkBundle\Controller\Controller; 11use Symfony\Bundle\FrameworkBundle\Controller\Controller;
10use Wallabag\CoreBundle\Entity\Entry; 12use Wallabag\CoreBundle\Entity\Entry;
11use Wallabag\UserBundle\Entity\User; 13use Wallabag\UserBundle\Entity\User;
14use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
12 15
13class RssController extends Controller 16class RssController extends Controller
14{ 17{
@@ -20,9 +23,9 @@ class RssController extends Controller
20 * 23 *
21 * @return \Symfony\Component\HttpFoundation\Response 24 * @return \Symfony\Component\HttpFoundation\Response
22 */ 25 */
23 public function showUnreadAction(User $user) 26 public function showUnreadAction(Request $request, User $user)
24 { 27 {
25 return $this->showEntries('unread', $user); 28 return $this->showEntries('unread', $user, $request->query->get('page', 1));
26 } 29 }
27 30
28 /** 31 /**
@@ -33,9 +36,9 @@ class RssController extends Controller
33 * 36 *
34 * @return \Symfony\Component\HttpFoundation\Response 37 * @return \Symfony\Component\HttpFoundation\Response
35 */ 38 */
36 public function showArchiveAction(User $user) 39 public function showArchiveAction(Request $request, User $user)
37 { 40 {
38 return $this->showEntries('archive', $user); 41 return $this->showEntries('archive', $user, $request->query->get('page', 1));
39 } 42 }
40 43
41 /** 44 /**
@@ -46,9 +49,9 @@ class RssController extends Controller
46 * 49 *
47 * @return \Symfony\Component\HttpFoundation\Response 50 * @return \Symfony\Component\HttpFoundation\Response
48 */ 51 */
49 public function showStarredAction(User $user) 52 public function showStarredAction(Request $request, User $user)
50 { 53 {
51 return $this->showEntries('starred', $user); 54 return $this->showEntries('starred', $user, $request->query->get('page', 1));
52 } 55 }
53 56
54 /** 57 /**
@@ -57,10 +60,11 @@ class RssController extends Controller
57 * 60 *
58 * @param string $type Entries type: unread, starred or archive 61 * @param string $type Entries type: unread, starred or archive
59 * @param User $user 62 * @param User $user
63 * @param int $page
60 * 64 *
61 * @return \Symfony\Component\HttpFoundation\Response 65 * @return \Symfony\Component\HttpFoundation\Response
62 */ 66 */
63 private function showEntries($type, User $user) 67 private function showEntries($type, User $user, $page = 1)
64 { 68 {
65 $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); 69 $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry');
66 70
@@ -87,8 +91,26 @@ class RssController extends Controller
87 $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit'); 91 $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit');
88 $entries->setMaxPerPage($perPage); 92 $entries->setMaxPerPage($perPage);
89 93
94 $url = $this->generateUrl(
95 $type.'_rss',
96 [
97 'username' => $user->getUsername(),
98 'token' => $user->getConfig()->getRssToken(),
99 ],
100 UrlGeneratorInterface::ABSOLUTE_URL
101 );
102
103 try {
104 $entries->setCurrentPage((int) $page);
105 } catch (OutOfRangeCurrentPageException $e) {
106 if ($page > 1) {
107 return $this->redirect($url.'?page='.$entries->getNbPages(), 302);
108 }
109 }
110
90 return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ 111 return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [
91 'type' => $type, 112 'type' => $type,
113 'url' => $url,
92 'entries' => $entries, 114 'entries' => $entries,
93 ]); 115 ]);
94 } 116 }
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/Form/Type/SearchEntryType.php b/src/Wallabag/CoreBundle/Form/Type/SearchEntryType.php
new file mode 100644
index 00000000..b56cae8e
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/SearchEntryType.php
@@ -0,0 +1,29 @@
1<?php
2
3namespace Wallabag\CoreBundle\Form\Type;
4
5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\TextType;
7use Symfony\Component\Form\FormBuilderInterface;
8use Symfony\Component\OptionsResolver\OptionsResolver;
9
10class SearchEntryType extends AbstractType
11{
12 public function buildForm(FormBuilderInterface $builder, array $options)
13 {
14 $builder
15 ->setMethod('GET')
16 ->add('term', TextType::class, [
17 'required' => true,
18 'label' => 'entry.new.form_search.term_label',
19 ])
20 ;
21 }
22
23 public function configureOptions(OptionsResolver $resolver)
24 {
25 $resolver->setDefaults([
26 'csrf_protection' => false,
27 ]);
28 }
29}
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..47e24d6b 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -86,6 +86,36 @@ class EntryRepository extends EntityRepository
86 } 86 }
87 87
88 /** 88 /**
89 * Retrieves entries filtered with a search term for a user.
90 *
91 * @param int $userId
92 * @param string $term
93 * @param strint $currentRoute
94 *
95 * @return QueryBuilder
96 */
97 public function getBuilderForSearchByUser($userId, $term, $currentRoute)
98 {
99 $qb = $this
100 ->getBuilderByUser($userId);
101
102 if ('starred' === $currentRoute) {
103 $qb->andWhere('e.isStarred = true');
104 } elseif ('unread' === $currentRoute) {
105 $qb->andWhere('e.isArchived = false');
106 } elseif ('archive' === $currentRoute) {
107 $qb->andWhere('e.isArchived = true');
108 }
109
110 $qb
111 ->andWhere('e.content LIKE :term OR e.title LIKE :term')->setParameter('term', '%'.$term.'%')
112 ->leftJoin('e.tags', 't')
113 ->groupBy('e.id');
114
115 return $qb;
116 }
117
118 /**
89 * Retrieves untagged entries for a user. 119 * Retrieves untagged entries for a user.
90 * 120 *
91 * @param int $userId 121 * @param int $userId
@@ -329,4 +359,18 @@ class EntryRepository extends EntityRepository
329 359
330 return $qb->getQuery()->getSingleScalarResult(); 360 return $qb->getQuery()->getSingleScalarResult();
331 } 361 }
362
363 /**
364 * Remove all entries for a user id.
365 * Used when a user want to reset all informations.
366 *
367 * @param int $userId
368 */
369 public function removeAllByUserId($userId)
370 {
371 $this->getEntityManager()
372 ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId')
373 ->setParameter('userId', $userId)
374 ->execute();
375 }
332} 376}
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..0280bc18 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
@@ -109,9 +110,38 @@ services:
109 host: '%redis_host%' 110 host: '%redis_host%'
110 port: '%redis_port%' 111 port: '%redis_port%'
111 path: '%redis_path%' 112 path: '%redis_path%'
113 password: '%redis_password%'
112 114
113 wallabag_core.exception_controller: 115 wallabag_core.exception_controller:
114 class: Wallabag\CoreBundle\Controller\ExceptionController 116 class: Wallabag\CoreBundle\Controller\ExceptionController
115 arguments: 117 arguments:
116 - '@twig' 118 - '@twig'
117 - '%kernel.debug%' 119 - '%kernel.debug%'
120
121 wallabag_core.subscriber.sqlite_cascade_delete:
122 class: Wallabag\CoreBundle\Event\Subscriber\SQLiteCascadeDeleteSubscriber
123 arguments:
124 - "@doctrine"
125 tags:
126 - { name: doctrine.event_subscriber }
127
128 wallabag_core.subscriber.download_images:
129 class: Wallabag\CoreBundle\Event\Subscriber\DownloadImagesSubscriber
130 arguments:
131 - "@doctrine.orm.default_entity_manager"
132 - "@wallabag_core.entry.download_images"
133 - '@=service(''craue_config'').get(''download_images_enabled'')'
134 - "@logger"
135 tags:
136 - { name: kernel.event_subscriber }
137
138 wallabag_core.entry.download_images:
139 class: Wallabag\CoreBundle\Helper\DownloadImages
140 arguments:
141 - "@wallabag_core.entry.download_images.client"
142 - "%kernel.root_dir%/../web/assets/images"
143 - '@=service(''craue_config'').get(''wallabag_url'')'
144 - "@logger"
145
146 wallabag_core.entry.download_images.client:
147 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..d6c294a4 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'
@@ -145,6 +162,7 @@ entry:
145 # archived: 'Archived entries' 162 # archived: 'Archived entries'
146 # filtered: 'Filtered entries' 163 # filtered: 'Filtered entries'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' 168 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Har et vist billede' 186 preview_picture_label: 'Har et vist billede'
169 preview_picture_help: 'Forhåndsvis billede' 187 preview_picture_help: 'Forhåndsvis billede'
170 language_label: 'Sprog' 188 language_label: 'Sprog'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Læsetid i minutter' 191 label: 'Læsetid i minutter'
173 from: 'fra' 192 from: 'fra'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 # page_title: 'Edit an entry' 234 # page_title: 'Edit an entry'
214 # title_label: 'Title' 235 # title_label: 'Title'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 351 # 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' 352 # see_untagged_entries: 'See untagged entries'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 # page_title: 'Import' 358 # page_title: 'Import'
@@ -362,6 +386,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 387 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:" 388 # 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:"
389 # 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: 390 # firefox:
366 # page_title: 'Import > Firefox' 391 # 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." 392 # 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 +399,10 @@ import:
374 # page_title: 'Import > Instapaper' 399 # 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").' 400 # 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.' 401 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 # page_title: "Import > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 # page_title: 'Developer' 408 # page_title: 'Developer'
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'RSS-oplysninger opdateret' 494 rss_updated: 'RSS-oplysninger opdateret'
466 # tagging_rules_updated: 'Tagging rules updated' 495 # tagging_rules_updated: 'Tagging rules updated'
467 # tagging_rules_deleted: 'Tagging rule deleted' 496 # tagging_rules_deleted: 'Tagging rule deleted'
468 # user_added: 'User "%username%" added'
469 # rss_token_updated: 'RSS token updated' 497 # rss_token_updated: 'RSS token updated'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
470 entry: 501 entry:
471 notice: 502 notice:
472 # entry_already_saved: 'Entry already saved on %date%' 503 # entry_already_saved: 'Entry already saved on %date%'
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 # client_created: 'New client created.' 528 # client_created: 'New client created.'
498 # client_deleted: 'Client deleted' 529 # client_deleted: 'Client deleted'
530 user:
531 notice:
532 # added: 'User "%username%" added'
533 # updated: 'User "%username%" updated'
534 # 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..8a67e0df 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'
@@ -145,6 +162,7 @@ entry:
145 archived: 'Archivierte Einträge' 162 archived: 'Archivierte Einträge'
146 filtered: 'Gefilterte Einträge' 163 filtered: 'Gefilterte Einträge'
147 filtered_tags: 'Gefiltert nach Tags:' 164 filtered_tags: 'Gefiltert nach Tags:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Nicht getaggte Einträge' 166 untagged: 'Nicht getaggte Einträge'
149 list: 167 list:
150 number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.' 168 number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Vorschaubild vorhanden' 186 preview_picture_label: 'Vorschaubild vorhanden'
169 preview_picture_help: 'Vorschaubild' 187 preview_picture_help: 'Vorschaubild'
170 language_label: 'Sprache' 188 language_label: 'Sprache'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Lesezeit in Minuten' 191 label: 'Lesezeit in Minuten'
173 from: 'von' 192 from: 'von'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'https://website.de' 228 placeholder: 'https://website.de'
210 form_new: 229 form_new:
211 url_label: URL 230 url_label: URL
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Eintrag bearbeiten' 234 page_title: 'Eintrag bearbeiten'
214 title_label: 'Titel' 235 title_label: 'Titel'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: '{0} Es gibt keine Tags.|{1} Es gibt einen Tag.|]1,Inf[ Es gibt %count% Tags.' 351 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' 352 see_untagged_entries: 'Zeige nicht getaggte Einträge'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 page_title: 'Importieren' 358 page_title: 'Importieren'
@@ -362,6 +386,7 @@ import:
362 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.' 386 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.'
363 worker: 387 worker:
364 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:" 388 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:"
389 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: 390 firefox:
366 page_title: 'Aus Firefox importieren' 391 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." 392 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 +399,10 @@ import:
374 page_title: 'Aus Instapaper importieren' 399 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").' 400 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." 401 how_to: "Bitte wähle deine Instapaper Sicherungsdatei aus und klicke den nachfolgenden Button zum Importieren."
402 pinboard:
403 page_title: "Aus Pinboard importieren"
404 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").'
405 how_to: 'Bitte wähle deinen Pinboard Export aus und klicke den nachfolgenden Button zum Importieren.'
377 406
378developer: 407developer:
379 page_title: 'Entwickler' 408 page_title: 'Entwickler'
@@ -453,7 +482,7 @@ user:
453 back_to_list: Zurück zur Liste 482 back_to_list: Zurück zur Liste
454 483
455error: 484error:
456 # page_title: An error occurred 485 page_title: Ein Fehler ist aufgetreten
457 486
458flashes: 487flashes:
459 config: 488 config:
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'RSS-Informationen aktualisiert' 494 rss_updated: 'RSS-Informationen aktualisiert'
466 tagging_rules_updated: 'Tagging-Regeln aktualisiert' 495 tagging_rules_updated: 'Tagging-Regeln aktualisiert'
467 tagging_rules_deleted: 'Tagging-Regel gelöscht' 496 tagging_rules_deleted: 'Tagging-Regel gelöscht'
468 user_added: 'Benutzer "%username%" erstellt'
469 rss_token_updated: 'RSS-Token aktualisiert' 497 rss_token_updated: 'RSS-Token aktualisiert'
498 annotations_reset: Anmerkungen zurücksetzen
499 tags_reset: Tags zurücksetzen
500 entries_reset: Einträge zurücksetzen
470 entry: 501 entry:
471 notice: 502 notice:
472 entry_already_saved: 'Eintrag bereits am %date% gespeichert' 503 entry_already_saved: 'Eintrag bereits am %date% gespeichert'
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 client_created: 'Neuer Client erstellt.' 528 client_created: 'Neuer Client erstellt.'
498 client_deleted: 'Client gelöscht' 529 client_deleted: 'Client gelöscht'
530 user:
531 notice:
532 added: 'Benutzer "%username%" hinzugefügt'
533 updated: 'Benutzer "%username%" aktualisiert'
534 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..63216a0f 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'
@@ -145,6 +162,7 @@ entry:
145 archived: 'Archived entries' 162 archived: 'Archived entries'
146 filtered: 'Filtered entries' 163 filtered: 'Filtered entries'
147 filtered_tags: 'Filtered by tags:' 164 filtered_tags: 'Filtered by tags:'
165 filtered_search: 'Filtered by search:'
148 untagged: 'Untagged entries' 166 untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.' 168 number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Has a preview picture' 186 preview_picture_label: 'Has a preview picture'
169 preview_picture_help: 'Preview picture' 187 preview_picture_help: 'Preview picture'
170 language_label: 'Language' 188 language_label: 'Language'
189 http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Reading time in minutes' 191 label: 'Reading time in minutes'
173 from: 'from' 192 from: 'from'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Edit an entry' 234 page_title: 'Edit an entry'
214 title_label: 'Title' 235 title_label: 'Title'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: '{0} There are no tags.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 351 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' 352 see_untagged_entries: 'See untagged entries'
353 new:
354 add: 'Add'
355 placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 page_title: 'Import' 358 page_title: 'Import'
@@ -362,6 +386,7 @@ import:
362 how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 387 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:" 388 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:"
389 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: 390 firefox:
366 page_title: 'Import > Firefox' 391 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." 392 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 +399,10 @@ import:
374 page_title: 'Import > Instapaper' 399 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").' 400 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.' 401 how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 page_title: "Import > Pinboard"
404 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").'
405 how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 page_title: 'Developer' 408 page_title: 'Developer'
@@ -466,6 +495,9 @@ flashes:
466 tagging_rules_updated: 'Tagging rules updated' 495 tagging_rules_updated: 'Tagging rules updated'
467 tagging_rules_deleted: 'Tagging rule deleted' 496 tagging_rules_deleted: 'Tagging rule deleted'
468 rss_token_updated: 'RSS token updated' 497 rss_token_updated: 'RSS token updated'
498 annotations_reset: Annotations reset
499 tags_reset: Tags reset
500 entries_reset: Entries reset
469 entry: 501 entry:
470 notice: 502 notice:
471 entry_already_saved: 'Entry already saved on %date%' 503 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..3876725e 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'
@@ -145,6 +162,7 @@ entry:
145 archived: 'Artículos archivados' 162 archived: 'Artículos archivados'
146 filtered: 'Artículos filtrados' 163 filtered: 'Artículos filtrados'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.' 168 number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Hay una foto' 186 preview_picture_label: 'Hay una foto'
169 preview_picture_help: 'Foto de preview' 187 preview_picture_help: 'Foto de preview'
170 language_label: 'Idioma' 188 language_label: 'Idioma'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Duración de lectura en minutos' 191 label: 'Duración de lectura en minutos'
173 from: 'de' 192 from: 'de'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Editar un artículo' 234 page_title: 'Editar un artículo'
214 title_label: 'Título' 235 title_label: 'Título'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: '{0} No hay ninguna etiqueta.|{1} Hay una etiqueta.|]1,Inf[ Hay %count% etiquetas.' 351 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' 352 # see_untagged_entries: 'See untagged entries'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 page_title: 'Importar' 358 page_title: 'Importar'
@@ -362,6 +386,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 387 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:" 388 # 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:"
389 # 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: 390 firefox:
366 page_title: 'Importar > Firefox' 391 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." 392 # 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 +399,10 @@ import:
374 page_title: 'Importar > Instapaper' 399 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").' 400 # 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.' 401 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 page_title: "Importar > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 page_title: 'Promotor' 408 page_title: 'Promotor'
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'La configuración de los feeds RSS ha sido actualizada' 494 rss_updated: 'La configuración de los feeds RSS ha sido actualizada'
466 tagging_rules_updated: 'Regla de etiquetado borrada' 495 tagging_rules_updated: 'Regla de etiquetado borrada'
467 tagging_rules_deleted: 'Regla de etiquetado actualizada' 496 tagging_rules_deleted: 'Regla de etiquetado actualizada'
468 user_added: 'Usuario "%username%" añadido'
469 rss_token_updated: 'RSS token actualizado' 497 rss_token_updated: 'RSS token actualizado'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
470 entry: 501 entry:
471 notice: 502 notice:
472 entry_already_saved: 'Entrada ya guardada por %fecha%' 503 entry_already_saved: 'Entrada ya guardada por %fecha%'
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 client_created: 'Nuevo cliente creado.' 528 client_created: 'Nuevo cliente creado.'
498 client_deleted: 'Cliente suprimido' 529 client_deleted: 'Cliente suprimido'
530 user:
531 notice:
532 # added: 'User "%username%" added'
533 # updated: 'User "%username%" updated'
534 # 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..301668e3 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: 'رمز قدیمی'
@@ -145,6 +162,7 @@ entry:
145 archived: 'مقاله‌های بایگانی‌شده' 162 archived: 'مقاله‌های بایگانی‌شده'
146 filtered: 'مقاله‌های فیلترشده' 163 filtered: 'مقاله‌های فیلترشده'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: '{0} هیج مقاله‌ای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.' 168 number_on_the_page: '{0} هیج مقاله‌ای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'دارای عکس پیش‌نمایش' 186 preview_picture_label: 'دارای عکس پیش‌نمایش'
169 preview_picture_help: 'پیش‌نمایش عکس' 187 preview_picture_help: 'پیش‌نمایش عکس'
170 language_label: 'زبان' 188 language_label: 'زبان'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'زمان خواندن به دقیقه' 191 label: 'زمان خواندن به دقیقه'
173 from: 'از' 192 from: 'از'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: نشانی 230 url_label: نشانی
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'ویرایش مقاله' 234 page_title: 'ویرایش مقاله'
214 title_label: 'عنوان' 235 title_label: 'عنوان'
@@ -279,6 +300,7 @@ quickstart:
279 paragraph_2: 'ادامه دهید!' 300 paragraph_2: 'ادامه دهید!'
280 configure: 301 configure:
281 title: 'برنامه را تنظیم کنید' 302 title: 'برنامه را تنظیم کنید'
303 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.'
282 language: 'زبان و نمای برنامه را تغییر دهید' 304 language: 'زبان و نمای برنامه را تغییر دهید'
283 rss: 'خوراک آر-اس-اس را فعال کنید' 305 rss: 'خوراک آر-اس-اس را فعال کنید'
284 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید' 306 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید'
@@ -328,6 +350,9 @@ tag:
328 list: 350 list:
329 number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.' 351 number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.'
330 # see_untagged_entries: 'See untagged entries' 352 # see_untagged_entries: 'See untagged entries'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
331 356
332import: 357import:
333 page_title: 'درون‌ریزی' 358 page_title: 'درون‌ریزی'
@@ -361,6 +386,7 @@ import:
361 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
362 worker: 387 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:" 388 # 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:"
389 # 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: 390 firefox:
365 page_title: 'درون‌ریزی > Firefox' 391 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." 392 # 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 +399,10 @@ import:
373 page_title: 'درون‌ریزی > Instapaper' 399 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").' 400 # 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.' 401 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 # page_title: "Import > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
376 406
377developer: 407developer:
378 # page_title: 'Developer' 408 # page_title: 'Developer'
@@ -464,8 +494,10 @@ flashes:
464 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد' 494 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد'
465 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد' 495 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد'
466 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد' 496 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد'
467 user_added: 'کابر "%username%" افزوده شد'
468 rss_token_updated: 'کد آر-اس-اس به‌روز شد' 497 rss_token_updated: 'کد آر-اس-اس به‌روز شد'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
469 entry: 501 entry:
470 notice: 502 notice:
471 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' 503 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود'
@@ -495,3 +527,8 @@ flashes:
495 notice: 527 notice:
496 # client_created: 'New client created.' 528 # client_created: 'New client created.'
497 # client_deleted: 'Client deleted' 529 # client_deleted: 'Client deleted'
530 user:
531 notice:
532 # added: 'User "%username%" added'
533 # updated: 'User "%username%" updated'
534 # 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..d97eab26 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"
@@ -145,6 +162,7 @@ entry:
145 archived: "Articles lus" 162 archived: "Articles lus"
146 filtered: "Articles filtrés" 163 filtered: "Articles filtrés"
147 filtered_tags: "Articles filtrés par tags :" 164 filtered_tags: "Articles filtrés par tags :"
165 filtered_search: 'Articles filtrés par recherche :'
148 untagged: "Article sans tag" 166 untagged: "Article sans tag"
149 list: 167 list:
150 number_on_the_page: "{0} Il n’y a pas d’articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles." 168 number_on_the_page: "{0} Il n’y a pas d’articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles."
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: "A une photo" 186 preview_picture_label: "A une photo"
169 preview_picture_help: "Photo" 187 preview_picture_help: "Photo"
170 language_label: "Langue" 188 language_label: "Langue"
189 http_status_label: 'Statut HTTP'
171 reading_time: 190 reading_time:
172 label: "Durée de lecture en minutes" 191 label: "Durée de lecture en minutes"
173 from: "de" 192 from: "de"
@@ -209,6 +228,8 @@ entry:
209 placeholder: "http://website.com" 228 placeholder: "http://website.com"
210 form_new: 229 form_new:
211 url_label: "Adresse" 230 url_label: "Adresse"
231 search:
232 placeholder: "Que recherchez-vous ?"
212 edit: 233 edit:
213 page_title: "Éditer un article" 234 page_title: "Éditer un article"
214 title_label: "Titre" 235 title_label: "Titre"
@@ -329,6 +350,9 @@ tag:
329 list: 350 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." 351 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" 352 see_untagged_entries: "Voir les articles sans tag"
353 new:
354 add: 'Ajouter'
355 placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.'
332 356
333import: 357import:
334 page_title: "Importer" 358 page_title: "Importer"
@@ -362,6 +386,7 @@ import:
362 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." 386 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer."
363 worker: 387 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 :" 388 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :"
389 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: 390 firefox:
366 page_title: "Import > Firefox" 391 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>" 392 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 +399,10 @@ import:
374 page_title: "Import > Instapaper" 399 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 »)." 400 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." 401 how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l’importer."
402 pinboard:
403 page_title: "Import > Pinboard"
404 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 »)."
405 how_to: "Choisissez le fichier de votre export Pinboard et cliquez sur le bouton ci-dessous pour l’importer."
377 406
378developer: 407developer:
379 page_title: "Développeur" 408 page_title: "Développeur"
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: "La configuration des flux RSS a bien été mise à jour" 494 rss_updated: "La configuration des flux RSS a bien été mise à jour"
466 tagging_rules_updated: "Règles mises à jour" 495 tagging_rules_updated: "Règles mises à jour"
467 tagging_rules_deleted: "Règle supprimée" 496 tagging_rules_deleted: "Règle supprimée"
468 user_added: "Utilisateur \"%username%\" ajouté"
469 rss_token_updated: "Jeton RSS mis à jour" 497 rss_token_updated: "Jeton RSS mis à jour"
498 annotations_reset: Annotations supprimées
499 tags_reset: Tags supprimés
500 entries_reset: Articles supprimés
470 entry: 501 entry:
471 notice: 502 notice:
472 entry_already_saved: "Article déjà sauvegardé le %date%" 503 entry_already_saved: "Article déjà sauvegardé le %date%"
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 client_created: "Nouveau client %name% créé" 528 client_created: "Nouveau client %name% créé"
498 client_deleted: "Client %name% supprimé" 529 client_deleted: "Client %name% supprimé"
530 user:
531 notice:
532 added: 'Utilisateur "%username%" ajouté'
533 updated: 'Utilisateur "%username%" mis à jour'
534 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..4be99a27 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'
@@ -145,6 +162,7 @@ entry:
145 archived: 'Contenuti archiviati' 162 archived: 'Contenuti archiviati'
146 filtered: 'Contenuti filtrati' 163 filtered: 'Contenuti filtrati'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti." 168 number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti."
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: "Ha un'immagine di anteprima" 186 preview_picture_label: "Ha un'immagine di anteprima"
169 preview_picture_help: 'Immagine di anteprima' 187 preview_picture_help: 'Immagine di anteprima'
170 language_label: 'Lingua' 188 language_label: 'Lingua'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Tempo di lettura in minuti' 191 label: 'Tempo di lettura in minuti'
173 from: 'da' 192 from: 'da'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Modifica voce' 234 page_title: 'Modifica voce'
214 title_label: 'Titolo' 235 title_label: 'Titolo'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: "{0} Non ci sono tag.|{1} C'è un tag.|]1,Inf[ ci sono %count% tag." 351 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' 352 # see_untagged_entries: 'See untagged entries'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 page_title: 'Importa' 358 page_title: 'Importa'
@@ -362,6 +386,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 387 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:" 388 # 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:"
389 # 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: 390 firefox:
366 page_title: 'Importa da > Firefox' 391 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." 392 # 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 +399,10 @@ import:
374 page_title: 'Importa da > Instapaper' 399 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").' 400 # 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.' 401 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 page_title: "Importa da > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 page_title: 'Sviluppatori' 408 page_title: 'Sviluppatori'
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'Informazioni RSS aggiornate' 494 rss_updated: 'Informazioni RSS aggiornate'
466 tagging_rules_updated: 'Regole di tagging aggiornate' 495 tagging_rules_updated: 'Regole di tagging aggiornate'
467 tagging_rules_deleted: 'Regola di tagging aggiornate' 496 tagging_rules_deleted: 'Regola di tagging aggiornate'
468 user_added: 'Utente "%username%" aggiunto'
469 rss_token_updated: 'RSS token aggiornato' 497 rss_token_updated: 'RSS token aggiornato'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
470 entry: 501 entry:
471 notice: 502 notice:
472 entry_already_saved: 'Contenuto già salvato in data %date%' 503 entry_already_saved: 'Contenuto già salvato in data %date%'
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 client_created: 'Nuovo client creato.' 528 client_created: 'Nuovo client creato.'
498 client_deleted: 'Client eliminato' 529 client_deleted: 'Client eliminato'
530 user:
531 notice:
532 # added: 'User "%username%" added'
533 # updated: 'User "%username%" updated'
534 # 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..5ded91f9 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:
@@ -145,6 +162,7 @@ entry:
145 archived: 'Articles legits' 162 archived: 'Articles legits'
146 filtered: 'Articles filtrats' 163 filtered: 'Articles filtrats'
147 filtered_tags: 'Filtats per etiquetas:' 164 filtered_tags: 'Filtats per etiquetas:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Articles sens etiqueta' 166 untagged: 'Articles sens etiqueta'
149 list: 167 list:
150 number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles." 168 number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles."
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'A una fotò' 186 preview_picture_label: 'A una fotò'
169 preview_picture_help: 'Fotò' 187 preview_picture_help: 'Fotò'
170 language_label: 'Lenga' 188 language_label: 'Lenga'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Durada de lectura en minutas' 191 label: 'Durada de lectura en minutas'
173 from: 'de' 192 from: 'de'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Modificar un article' 234 page_title: 'Modificar un article'
214 title_label: 'Títol' 235 title_label: 'Títol'
@@ -216,7 +237,7 @@ entry:
216 is_public_label: 'Public' 237 is_public_label: 'Public'
217 save_label: 'Enregistrar' 238 save_label: 'Enregistrar'
218 public: 239 public:
219 # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" 240 shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>"
220 241
221about: 242about:
222 page_title: 'A prepaus' 243 page_title: 'A prepaus'
@@ -272,14 +293,14 @@ howto:
272 293
273quickstart: 294quickstart:
274 page_title: 'Per ben començar' 295 page_title: 'Per ben començar'
275 # more: 'More…' 296 more: 'Mai…'
276 intro: 297 intro:
277 title: 'Benvenguda sus wallabag !' 298 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." 299 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 ' 300 paragraph_2: 'Seguètz-nos '
280 configure: 301 configure:
281 title: "Configuratz l'aplicacio" 302 title: "Configuratz l'aplicacion"
282 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.' 303 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" 304 language: "Cambiatz la lenga e l'estil de l'aplicacion"
284 rss: 'Activatz los fluxes RSS' 305 rss: 'Activatz los fluxes RSS'
285 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' 306 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles'
@@ -293,7 +314,7 @@ quickstart:
293 import: 'Configurar los impòrt' 314 import: 'Configurar los impòrt'
294 first_steps: 315 first_steps:
295 title: 'Primièrs passes' 316 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." 317 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' 318 new_article: 'Ajustatz vòstre primièr article'
298 unread_articles: 'E racaptatz-lo !' 319 unread_articles: 'E racaptatz-lo !'
299 migrate: 320 migrate:
@@ -305,14 +326,14 @@ quickstart:
305 readability: 'Migrar dempuèi Readability' 326 readability: 'Migrar dempuèi Readability'
306 instapaper: 'Migrar dempuèi Instapaper' 327 instapaper: 'Migrar dempuèi Instapaper'
307 developer: 328 developer:
308 title: 'Pels desvolopadors' 329 title: 'Pels desvolopaires'
309 # description: 'We also thought to the developers: Docker, API, translations, etc.' 330 description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.'
310 create_application: 'Crear vòstra aplicacion tèrça' 331 create_application: 'Crear vòstra aplicacion tèrça'
311 # use_docker: 'Use Docker to install wallabag' 332 use_docker: 'Utilizar Docker per installar wallabag'
312 docs: 333 docs:
313 title: 'Documentacion complèta' 334 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." 335 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' 336 annotate: 'Anotar vòstre article'
316 export: 'Convertissètz vòstres articles en ePub o en PDF' 337 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" 338 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 ?" 339 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?"
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas." 351 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" 352 see_untagged_entries: "Afichar las entradas sens pas cap d'etiquetas"
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 page_title: 'Importar' 358 page_title: 'Importar'
@@ -362,6 +386,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." 386 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: 387 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 : " 388 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 : "
389 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: 390 firefox:
366 page_title: 'Importar > Firefox' 391 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." 392 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 +399,10 @@ import:
374 page_title: 'Importar > Instapaper' 399 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\")." 400 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" 401 how_to: "Mercés de causir vòstre fichièr Instapaper e de clicar sul boton dejós per lo telecargar e l'importar"
402 pinboard:
403 # page_title: "Import > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 page_title: 'Desvolopaire' 408 page_title: 'Desvolopaire'
@@ -397,7 +426,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." 426 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' 427 action: 'Suprimir aqueste client'
399 client: 428 client:
400 page_title: 'Desvlopador > Novèl client' 429 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." 430 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion."
402 form: 431 form:
403 name_label: "Nom del client" 432 name_label: "Nom del client"
@@ -405,7 +434,7 @@ developer:
405 save_label: 'Crear un novèl client' 434 save_label: 'Crear un novèl client'
406 action_back: 'Retorn' 435 action_back: 'Retorn'
407 client_parameter: 436 client_parameter:
408 page_title: 'Desvolopador > Los paramètres de vòstre client' 437 page_title: 'Desvolopaire > Los paramètres de vòstre client'
409 page_description: 'Vaquí los paramètres de vòstre client' 438 page_description: 'Vaquí los paramètres de vòstre client'
410 field_name: 'Nom del client' 439 field_name: 'Nom del client'
411 field_id: 'ID Client' 440 field_id: 'ID Client'
@@ -413,7 +442,7 @@ developer:
413 back: 'Retour' 442 back: 'Retour'
414 read_howto: 'Legir "cossí crear ma primièra aplicacion"' 443 read_howto: 'Legir "cossí crear ma primièra aplicacion"'
415 howto: 444 howto:
416 page_title: 'Desvolopador > Cossí crear ma primièra aplicacion' 445 page_title: 'Desvolopaire > Cossí crear ma primièra aplicacion'
417 description: 446 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." 447 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." 448 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar."
@@ -426,34 +455,34 @@ developer:
426 back: 'Retorn' 455 back: 'Retorn'
427 456
428user: 457user:
429 # page_title: Users management 458 page_title: 'Gestion dels utilizaires'
430 # new_user: Create a new user 459 new_user: 'Crear un novèl utilizaire'
431 # edit_user: Edit an existing user 460 edit_user: 'Modificar un utilizaire existent'
432 # description: "Here you can manage all users (create, edit and delete)" 461 description: "Aquí podètz gerir totes los utilizaires (crear, modificar e suprimir)"
433 # list: 462 list:
434 # actions: Actions 463 actions: 'Accions'
435 # edit_action: Edit 464 edit_action: 'Modificar'
436 # yes: Yes 465 yes: 'Òc'
437 # no: No 466 no: 'Non'
438 # create_new_one: Create a new user 467 create_new_one: 'Crear un novèl utilizaire'
439 form: 468 form:
440 username_label: "Nom d'utilizaire" 469 username_label: "Nom d'utilizaire"
441 # name_label: 'Name' 470 name_label: 'Nom'
442 password_label: 'Senhal' 471 password_label: 'Senhal'
443 repeat_new_password_label: 'Confirmatz vòstre novèl senhal' 472 repeat_new_password_label: 'Confirmatz vòstre novèl senhal'
444 plain_password_label: 'Senhal en clar' 473 plain_password_label: 'Senhal en clar'
445 email_label: 'Adreça de corrièl' 474 email_label: 'Adreça de corrièl'
446 # enabled_label: 'Enabled' 475 enabled_label: 'Actiu'
447 # locked_label: 'Locked' 476 locked_label: 'Varrolhat'
448 # last_login_label: 'Last login' 477 last_login_label: 'Darrièra connexion'
449 # twofactor_label: Two factor authentication 478 twofactor_label: 'Autentificacion doble-factor'
450 # save: Save 479 save: 'Enregistrar'
451 # delete: Delete 480 delete: 'Suprimir'
452 # delete_confirm: Are you sure? 481 delete_confirm: 'Sètz segur ?'
453 # back_to_list: Back to list 482 back_to_list: 'Tornar a la lista'
454 483
455error: 484error:
456 # page_title: An error occurred 485 page_title: Una error s'es produsida
457 486
458flashes: 487flashes:
459 config: 488 config:
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' 494 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn'
466 tagging_rules_updated: 'Règlas misa a jorn' 495 tagging_rules_updated: 'Règlas misa a jorn'
467 tagging_rules_deleted: 'Règla suprimida' 496 tagging_rules_deleted: 'Règla suprimida'
468 user_added: 'Utilizaire "%username%" apondut'
469 rss_token_updated: 'Geton RSS mes a jorn' 497 rss_token_updated: 'Geton RSS mes a jorn'
498 annotations_reset: Anotacions levadas
499 tags_reset: Etiquetas levadas
500 entries_reset: Articles levats
470 entry: 501 entry:
471 notice: 502 notice:
472 entry_already_saved: 'Article ja salvargardat lo %date%' 503 entry_already_saved: 'Article ja salvargardat lo %date%'
@@ -477,12 +508,12 @@ flashes:
477 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat" 508 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' 509 entry_archived: 'Article marcat coma legit'
479 entry_unarchived: 'Article marcat coma pas legit' 510 entry_unarchived: 'Article marcat coma pas legit'
480 entry_starred: 'Article apondut dins los favorits' 511 entry_starred: 'Article ajustat dins los favorits'
481 entry_unstarred: 'Article quitat dels favorits' 512 entry_unstarred: 'Article quitat dels favorits'
482 entry_deleted: 'Article suprimit' 513 entry_deleted: 'Article suprimit'
483 tag: 514 tag:
484 notice: 515 notice:
485 tag_added: 'Etiqueta aponduda' 516 tag_added: 'Etiqueta ajustada'
486 import: 517 import:
487 notice: 518 notice:
488 failed: "L'importacion a fracassat, mercés de tornar ensajar" 519 failed: "L'importacion a fracassat, mercés de tornar ensajar"
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 client_created: 'Novèl client creat' 528 client_created: 'Novèl client creat'
498 client_deleted: 'Client suprimit' 529 client_deleted: 'Client suprimit'
530 user:
531 notice:
532 added: 'Utilizaire "%username%" ajustat'
533 updated: 'Utilizaire "%username%" mes a jorn'
534 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..38fc4ceb 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'
@@ -145,6 +162,7 @@ entry:
145 archived: 'Zarchiwizowane wpisy' 162 archived: 'Zarchiwizowane wpisy'
146 filtered: 'Odfiltrowane wpisy' 163 filtered: 'Odfiltrowane wpisy'
147 filtered_tags: 'Filtrowane po tagach:' 164 filtered_tags: 'Filtrowane po tagach:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Odtaguj wpisy' 166 untagged: 'Odtaguj wpisy'
149 list: 167 list:
150 number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.' 168 number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Posiada podgląd obrazu' 186 preview_picture_label: 'Posiada podgląd obrazu'
169 preview_picture_help: 'Podgląd obrazu' 187 preview_picture_help: 'Podgląd obrazu'
170 language_label: 'Język' 188 language_label: 'Język'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Czas czytania w minutach' 191 label: 'Czas czytania w minutach'
173 from: 'od' 192 from: 'od'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Edytuj wpis' 234 page_title: 'Edytuj wpis'
214 title_label: 'Tytuł' 235 title_label: 'Tytuł'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: '{0} Nie ma tagów.|{1} Jest jeden tag.|]1,Inf[ Są %count% tagi.' 351 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' 352 see_untagged_entries: 'Zobacz nieotagowane wpisy'
353 new:
354 add: 'Dodaj'
355 placeholder: 'Możesz dodać kilka tagów, oddzielając je przecinkami.'
332 356
333import: 357import:
334 page_title: 'Import' 358 page_title: 'Import'
@@ -362,6 +386,7 @@ import:
362 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.' 386 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.'
363 worker: 387 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:" 388 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:"
389 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: 390 firefox:
366 page_title: 'Import > Firefox' 391 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." 392 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 +399,10 @@ import:
374 page_title: 'Import > Instapaper' 399 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.' 400 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ć.' 401 how_to: 'Wybierz swój plik eksportu z Instapaper i kliknij poniższy przycisk, aby go załadować.'
402 pinboard:
403 page_title: "Import > Pinboard"
404 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".'
405 how_to: 'Wybierz swój plik eksportu z Pinboard i kliknij poniższy przycisk, aby go załadować.'
377 406
378developer: 407developer:
379 page_title: 'Deweloper' 408 page_title: 'Deweloper'
@@ -453,7 +482,7 @@ user:
453 back_to_list: Powrót do listy 482 back_to_list: Powrót do listy
454 483
455error: 484error:
456 # page_title: An error occurred 485 page_title: Wystąpił błąd
457 486
458flashes: 487flashes:
459 config: 488 config:
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'Informacje RSS zaktualizowane' 494 rss_updated: 'Informacje RSS zaktualizowane'
466 tagging_rules_updated: 'Reguły tagowania zaktualizowane' 495 tagging_rules_updated: 'Reguły tagowania zaktualizowane'
467 tagging_rules_deleted: 'Reguła tagowania usunięta' 496 tagging_rules_deleted: 'Reguła tagowania usunięta'
468 user_added: 'Użytkownik "%username%" dodany'
469 rss_token_updated: 'Token kanału RSS zaktualizowany' 497 rss_token_updated: 'Token kanału RSS zaktualizowany'
498 annotations_reset: Zresetuj adnotacje
499 tags_reset: Zresetuj tagi
500 entries_reset: Zresetuj wpisy
470 entry: 501 entry:
471 notice: 502 notice:
472 entry_already_saved: 'Wpis już został dodany %date%' 503 entry_already_saved: 'Wpis już został dodany %date%'
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 client_created: 'Nowy klient utworzony.' 528 client_created: 'Nowy klient utworzony.'
498 client_deleted: 'Klient usunięty' 529 client_deleted: 'Klient usunięty'
530 user:
531 notice:
532 added: 'Użytkownik "%username%" dodany'
533 updated: 'Użytkownik "%username%" zaktualizowany'
534 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..4084469c 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'
@@ -145,6 +162,7 @@ entry:
145 archived: 'Entradas arquivadas' 162 archived: 'Entradas arquivadas'
146 filtered: 'Entradas filtradas' 163 filtered: 'Entradas filtradas'
147 filtered_tags: 'Filtrar por tags:' 164 filtered_tags: 'Filtrar por tags:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Entradas sem tags' 166 untagged: 'Entradas sem tags'
149 list: 167 list:
150 number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.' 168 number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Possui uma imagem de preview' 186 preview_picture_label: 'Possui uma imagem de preview'
169 preview_picture_help: 'Imagem de preview' 187 preview_picture_help: 'Imagem de preview'
170 language_label: 'Idioma' 188 language_label: 'Idioma'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Tempo de leitura em minutos' 191 label: 'Tempo de leitura em minutos'
173 from: 'de' 192 from: 'de'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Editar uma entrada' 234 page_title: 'Editar uma entrada'
214 title_label: 'Título' 235 title_label: 'Título'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.' 351 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' 352 see_untagged_entries: 'Ver entradas sem tags'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 page_title: 'Importar' 358 page_title: 'Importar'
@@ -362,6 +386,7 @@ import:
362 how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.' 386 how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.'
363 worker: 387 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 é:" 388 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 é:"
389 # 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: 390 firefox:
366 page_title: 'Importar > Firefox' 391 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." 392 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 +399,10 @@ import:
374 page_title: 'Importar > Instapaper' 399 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").' 400 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.' 401 how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.'
402 pinboard:
403 # page_title: "Import > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 page_title: 'Desenvolvedor' 408 page_title: 'Desenvolvedor'
@@ -452,17 +481,23 @@ user:
452 delete_confirm: 'Tem certeza?' 481 delete_confirm: 'Tem certeza?'
453 back_to_list: 'Voltar para a lista' 482 back_to_list: 'Voltar para a lista'
454 483
484error:
485 # page_title: An error occurred
486
455flashes: 487flashes:
456 config: 488 config:
457 notice: 489 notice:
458 config_saved: 'Configiração salva.' 490 config_saved: 'Configiração salva.'
459 password_updated: 'Senha atualizada' 491 password_updated: 'Senha atualizada'
460 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.' 492 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' 493 # user_updated: 'Information updated'
462 rss_updated: 'Informação de RSS atualizada' 494 rss_updated: 'Informação de RSS atualizada'
463 tagging_rules_updated: 'Regras de tags atualizadas' 495 tagging_rules_updated: 'Regras de tags atualizadas'
464 tagging_rules_deleted: 'Regra de tag apagada' 496 tagging_rules_deleted: 'Regra de tag apagada'
465 rss_token_updated: 'Token RSS atualizado' 497 rss_token_updated: 'Token RSS atualizado'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
466 entry: 501 entry:
467 notice: 502 notice:
468 entry_already_saved: 'Entrada já foi salva em %date%' 503 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..a6a7e146 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'
@@ -145,6 +162,7 @@ entry:
145 # archived: 'Archived entries' 162 # archived: 'Archived entries'
146 # filtered: 'Filtered entries' 163 # filtered: 'Filtered entries'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' 168 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Are o imagine de previzualizare' 186 preview_picture_label: 'Are o imagine de previzualizare'
169 preview_picture_help: 'Previzualizare imagine' 187 preview_picture_help: 'Previzualizare imagine'
170 language_label: 'Limbă' 188 language_label: 'Limbă'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Timp de citire în minute' 191 label: 'Timp de citire în minute'
173 from: 'de la' 192 from: 'de la'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 # page_title: 'Edit an entry' 234 # page_title: 'Edit an entry'
214 # title_label: 'Title' 235 # title_label: 'Title'
@@ -329,6 +350,9 @@ tag:
329 list: 350 list:
330 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 351 # 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' 352 # see_untagged_entries: 'See untagged entries'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
332 356
333import: 357import:
334 # page_title: 'Import' 358 # page_title: 'Import'
@@ -362,6 +386,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 387 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:" 388 # 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:"
389 # 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: 390 # firefox:
366 # page_title: 'Import > Firefox' 391 # 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." 392 # 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 +399,10 @@ import:
374 # page_title: 'Import > Instapaper' 399 # 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").' 400 # 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.' 401 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 # page_title: "Import > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 406
378developer: 407developer:
379 # page_title: 'Developer' 408 # page_title: 'Developer'
@@ -465,8 +494,10 @@ flashes:
465 rss_updated: 'Informație RSS actualizată' 494 rss_updated: 'Informație RSS actualizată'
466 # tagging_rules_updated: 'Tagging rules updated' 495 # tagging_rules_updated: 'Tagging rules updated'
467 # tagging_rules_deleted: 'Tagging rule deleted' 496 # tagging_rules_deleted: 'Tagging rule deleted'
468 # user_added: 'User "%username%" added'
469 # rss_token_updated: 'RSS token updated' 497 # rss_token_updated: 'RSS token updated'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
470 entry: 501 entry:
471 notice: 502 notice:
472 # entry_already_saved: 'Entry already saved on %date%' 503 # entry_already_saved: 'Entry already saved on %date%'
@@ -496,3 +527,8 @@ flashes:
496 notice: 527 notice:
497 # client_created: 'New client created.' 528 # client_created: 'New client created.'
498 # client_deleted: 'Client deleted' 529 # client_deleted: 'Client deleted'
530 user:
531 notice:
532 # added: 'User "%username%" added'
533 # updated: 'User "%username%" updated'
534 # 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..630ad0ca 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:
@@ -144,6 +162,7 @@ entry:
144 # archived: 'Archived entries' 162 # archived: 'Archived entries'
145 # filtered: 'Filtered entries' 163 # filtered: 'Filtered entries'
146 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
147 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
148 list: 167 list:
149 number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.' 168 number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.'
@@ -167,6 +186,7 @@ entry:
167 preview_picture_label: 'Resim önizlemesi varsa' 186 preview_picture_label: 'Resim önizlemesi varsa'
168 preview_picture_help: 'Resim önizlemesi' 187 preview_picture_help: 'Resim önizlemesi'
169 language_label: 'Dil' 188 language_label: 'Dil'
189 # http_status_label: 'HTTP status'
170 reading_time: 190 reading_time:
171 label: 'Dakika cinsinden okuma süresi' 191 label: 'Dakika cinsinden okuma süresi'
172 from: 'başlangıç' 192 from: 'başlangıç'
@@ -208,6 +228,8 @@ entry:
208 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
209 form_new: 229 form_new:
210 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
211 edit: 233 edit:
212 page_title: 'Makaleyi düzenle' 234 page_title: 'Makaleyi düzenle'
213 title_label: 'Başlık' 235 title_label: 'Başlık'
@@ -328,6 +350,9 @@ tag:
328 list: 350 list:
329 number_on_the_page: '{0} Herhangi bir etiket yok.|{1} Burada bir adet etiket var.|]1,Inf[ Burada %count% adet etiket var.' 351 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' 352 # see_untagged_entries: 'See untagged entries'
353 new:
354 # add: 'Add'
355 # placeholder: 'You can add several tags, separated by a comma.'
331 356
332import: 357import:
333 page_title: 'İçe Aktar' 358 page_title: 'İçe Aktar'
@@ -361,6 +386,7 @@ import:
361 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 386 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
362 worker: 387 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:" 388 # 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:"
389 # 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: 390 firefox:
365 page_title: 'İçe Aktar > Firefox' 391 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." 392 # 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 +399,10 @@ import:
373 page_title: 'İçe Aktar > Instapaper' 399 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").' 400 # 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.' 401 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
402 pinboard:
403 # page_title: "Import > Pinboard"
404 # 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").'
405 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
376 406
377developer: 407developer:
378 # page_title: 'Developer' 408 # page_title: 'Developer'
@@ -464,8 +494,10 @@ flashes:
464 rss_updated: 'RSS bilgiler güncellendi' 494 rss_updated: 'RSS bilgiler güncellendi'
465 tagging_rules_updated: 'Tagging rules updated' 495 tagging_rules_updated: 'Tagging rules updated'
466 tagging_rules_deleted: 'Tagging rule deleted' 496 tagging_rules_deleted: 'Tagging rule deleted'
467 user_added: 'User "%username%" added'
468 rss_token_updated: 'RSS token updated' 497 rss_token_updated: 'RSS token updated'
498 # annotations_reset: Annotations reset
499 # tags_reset: Tags reset
500 # entries_reset: Entries reset
469 entry: 501 entry:
470 notice: 502 notice:
471 entry_already_saved: 'Entry already saved on %date%' 503 entry_already_saved: 'Entry already saved on %date%'
@@ -495,3 +527,8 @@ flashes:
495 notice: 527 notice:
496 # client_created: 'New client created.' 528 # client_created: 'New client created.'
497 # client_deleted: 'Client deleted' 529 # client_deleted: 'Client deleted'
530 user:
531 notice:
532 # added: 'User "%username%" added'
533 # updated: 'User "%username%" updated'
534 # 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..a13fe903 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
@@ -1,11 +1,14 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %} 1{% extends "WallabagCoreBundle::layout.html.twig" %}
2 2
3{% block title %} 3{% block title %}
4 {% set currentTag = '' %} 4 {% set filter = '' %}
5 {% if tag is defined %} 5 {% if tag is defined %}
6 {% set currentTag = tag %} 6 {% set filter = tag %}
7 {% endif %} 7 {% endif %}
8 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %} 8 {% if searchTerm is defined and searchTerm is not empty %}
9 {% set filter = searchTerm %}
10 {% endif %}
11 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'filter': filter} %}
9{% endblock %} 12{% endblock %}
10 13
11{% block content %} 14{% block content %}
@@ -131,6 +134,13 @@
131 </div> 134 </div>
132 </div> 135 </div>
133 136
137 <div id="filter-http-status" class="filter-group">
138 {{ form_label(form.httpStatus) }}
139 <div class="input-field ">
140 {{ form_widget(form.httpStatus) }}
141 </div>
142 </div>
143
134 <div id="filter-reading-time" class="filter-group"> 144 <div id="filter-reading-time" class="filter-group">
135 <div class=""> 145 <div class="">
136 {{ form_label(form.readingTime) }} 146 {{ 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/baggy/Entry/search_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig
new file mode 100644
index 00000000..20821b6d
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig
@@ -0,0 +1,17 @@
1<form name="search" method="GET" action="{{ path('search')}}">
2 <h2>{{ 'menu.left.search'|trans }}</h2>
3 <a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">&times;</a>
4 {% if form_errors(form) %}
5 <span class="black-text">{{ form_errors(form) }}</span>
6 {% endif %}
7
8 {% if form_errors(form.term) %}
9 <span class="black-text">{{ form_errors(form.term) }}</span>
10 {% endif %}
11
12 <input type="hidden" name="currentRoute" value="{{ currentRoute }}" />
13
14 {{ form_widget(form.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'entry.search.placeholder'} }) }}
15
16 {{ form_rest(form) }}
17</form>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
index cd4ed3fa..07ff8e14 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
@@ -31,17 +31,11 @@
31 <li class="menu all"><a href="{{ path('all') }}">{{ 'menu.left.all_articles'|trans }}</a></li> 31 <li class="menu all"><a href="{{ path('all') }}">{{ 'menu.left.all_articles'|trans }}</a></li>
32 <li class="menu tag"><a href="{{ path('tag') }}">{{ 'menu.left.tags'|trans }}</a></li> 32 <li class="menu tag"><a href="{{ path('tag') }}">{{ 'menu.left.tags'|trans }}</a></li>
33 <li class="menu new"><a href="{{ path('new') }}">{{ 'menu.left.save_link'|trans }}</a></li> 33 <li class="menu new"><a href="{{ path('new') }}">{{ 'menu.left.save_link'|trans }}</a></li>
34 <!--<li style="position: relative;"><a href="javascript: void(null);" id="search">{{ 'menu.left.search'|trans }}</a> 34 <li style="position: relative;"><a href="javascript: void(null);" id="search">{{ 'menu.left.search'|trans }}</a>
35 <div id="search-form" class="messages info popup-form"> 35 <div id="search-form" class="messages info popup-form">
36 <form method="get" action="index.php"> 36 {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
37 <h2>{{ 'menu.left.search'|trans }}</h2>
38 <a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">&times;</a>
39 <input type="hidden" name="view" value="search">
40 <input required placeholder="{{ 'menu.search_form.input_label'|trans }}" type="text" name="search" id="searchfield"><br>
41 <input id="submit-search" type="submit" value="{{ 'menu.left.search'|trans }}">
42 </form>
43 </div> 37 </div>
44 </li>--> 38 </li>
45 <li class="menu config"><a href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a></li> 39 <li class="menu config"><a href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a></li>
46 {% if is_granted('ROLE_SUPER_ADMIN') %} 40 {% if is_granted('ROLE_SUPER_ADMIN') %}
47 <li class="menu users"><a href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a></li> 41 <li class="menu users"><a href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a></li>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig
index 92cabdd9..654c1d2d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig
@@ -6,8 +6,10 @@
6 {{ 'entry.page_titles.archived'|trans }} 6 {{ 'entry.page_titles.archived'|trans }}
7{% elseif currentRoute == 'all' %} 7{% elseif currentRoute == 'all' %}
8 {{ 'entry.page_titles.filtered'|trans }} 8 {{ 'entry.page_titles.filtered'|trans }}
9{% elseif currentRoute == 'search' %}
10 {{ 'entry.page_titles.filtered_search'|trans }} {{ filter }}
9{% elseif currentRoute == 'tag_entries' %} 11{% elseif currentRoute == 'tag_entries' %}
10 {{ 'entry.page_titles.filtered_tags'|trans }} {{ currentTag }} 12 {{ 'entry.page_titles.filtered_tags'|trans }} {{ filter }}
11{% elseif currentRoute == 'untagged' %} 13{% elseif currentRoute == 'untagged' %}
12 {{ 'entry.page_titles.untagged'|trans }} 14 {{ 'entry.page_titles.untagged'|trans }}
13{% else %} 15{% else %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
index 288bb54f..16ecaa97 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
@@ -2,7 +2,15 @@
2<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/"> 2<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
3 <channel> 3 <channel>
4 <title>wallabag — {{type}} feed</title> 4 <title>wallabag — {{type}} feed</title>
5 <link>{{ url('unread') }}</link> 5 <link>{{ url(type) }}</link>
6 <link rel="self" href="{{ app.request.uri }}"/>
7 {% if entries.hasPreviousPage -%}
8 <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/>
9 {% endif -%}
10 {% if entries.hasNextPage -%}
11 <link rel="next" href="{{ url }}?page={{ entries.nextPage }}"/>
12 {% endif -%}
13 <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/>
6 <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> 14 <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate>
7 <generator>wallabag</generator> 15 <generator>wallabag</generator>
8 <description>wallabag {{type}} elements</description> 16 <description>wallabag {{type}} elements</description>
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..00e8bf63 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
@@ -1,11 +1,14 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %} 1{% extends "WallabagCoreBundle::layout.html.twig" %}
2 2
3{% block title %} 3{% block title %}
4 {% set currentTag = '' %} 4 {% set filter = '' %}
5 {% if tag is defined %} 5 {% if tag is defined %}
6 {% set currentTag = tag %} 6 {% set filter = tag %}
7 {% endif %}
8 {% if searchTerm is defined and searchTerm is not empty %}
9 {% set filter = searchTerm %}
7 {% endif %} 10 {% endif %}
8 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %} 11 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'filter': filter} %}
9{% endblock %} 12{% endblock %}
10 13
11{% block content %} 14{% block content %}
@@ -104,6 +107,14 @@
104 </div> 107 </div>
105 108
106 <div class="col s12"> 109 <div class="col s12">
110 {{ form_label(form.httpStatus) }}
111 </div>
112
113 <div class="input-field col s12">
114 {{ form_widget(form.httpStatus) }}
115 </div>
116
117 <div class="col s12">
107 {{ form_label(form.readingTime) }} 118 {{ form_label(form.readingTime) }}
108 </div> 119 </div>
109 <div class="input-field col s6"> 120 <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/Entry/search_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig
new file mode 100644
index 00000000..f25de94d
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig
@@ -0,0 +1,15 @@
1<form name="search" method="GET" action="{{ path('search')}}">
2 {% if form_errors(form) %}
3 <span class="black-text">{{ form_errors(form) }}</span>
4 {% endif %}
5
6 {% if form_errors(form.term) %}
7 <span class="black-text">{{ form_errors(form.term) }}</span>
8 {% endif %}
9
10 <input type="hidden" name="currentRoute" value="{{ currentRoute }}" />
11
12 {{ form_widget(form.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'entry.search.placeholder'} }) }}
13
14 {{ form_rest(form) }}
15</form>
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/CoreBundle/Resources/views/themes/material/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
index f1ef01df..551486e0 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
@@ -89,11 +89,11 @@
89 <i class="material-icons">add</i> 89 <i class="material-icons">add</i>
90 </a> 90 </a>
91 </li> 91 </li>
92 <!--<li> 92 <li>
93 <a title="{{ 'menu.top.search'|trans }}" class="waves-effect" href="javascript: void(null);" id="nav-btn-search"> 93 <a title="{{ 'menu.top.search'|trans }}" class="waves-effect" href="javascript: void(null);" id="nav-btn-search">
94 <i class="material-icons">search</i> 94 <i class="material-icons">search</i>
95 </a> 95 </a>
96 </li>--> 96 </li>
97 <li id="button_filters"> 97 <li id="button_filters">
98 <a class="nav-panel-menu button-collapse-right tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters"> 98 <a class="nav-panel-menu button-collapse-right tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters">
99 <i class="material-icons">filter_list</i> 99 <i class="material-icons">filter_list</i>
@@ -106,13 +106,11 @@
106 </li> 106 </li>
107 </ul> 107 </ul>
108 </div> 108 </div>
109 <form method="get" action="index.php"> 109 <div class="input-field nav-panel-search" style="display: none">
110 <div class="input-field nav-panel-search" style="display: none"> 110 {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
111 <input name="search" id="searchfield" type="search" required placeholder="{{ 'menu.search_form.input_label'|trans }}"> 111 <label for="search" class="active"><i class="material-icons search">search</i></label>
112 <label for="search"><i class="material-icons search">search</i></label> 112 <i class="material-icons close">clear</i>
113 <i class="material-icons close">clear</i> 113 </div>
114 </div>
115 </form>
116 <div class="input-field nav-panel-add" style="display: none"> 114 <div class="input-field nav-panel-add" style="display: none">
117 {{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }} 115 {{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }}
118 <label for="add" class="active"><i class="material-icons add">add</i></label> 116 <label for="add" class="active"><i class="material-icons add">add</i></label>
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"