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/DeveloperController.php8
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php9
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php14
-rw-r--r--src/Wallabag/ApiBundle/Entity/Client.php17
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing_rest.yml13
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php37
-rw-r--r--src/Wallabag/CoreBundle/Controller/ConfigController.php112
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php14
-rw-r--r--src/Wallabag/CoreBundle/Controller/TagController.php10
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php7
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php6
-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)2
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php5
-rw-r--r--src/Wallabag/CoreBundle/Helper/DownloadImages.php233
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php14
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php18
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml30
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml23
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml23
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml17
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml23
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml24
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml23
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml23
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml99
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml25
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml1
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml23
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml24
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig41
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig40
-rw-r--r--src/Wallabag/ImportBundle/Command/ImportCommand.php26
-rw-r--r--src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php8
-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/PocketImport.php9
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/rabbit.yml7
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/redis.yml7
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/services.yml7
-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/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
59 files changed, 1305 insertions, 164 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/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..b3622c62 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{
@@ -200,9 +202,11 @@ class EntryRestController extends WallabagRestController
200 202
201 $em = $this->getDoctrine()->getManager(); 203 $em = $this->getDoctrine()->getManager();
202 $em->persist($entry); 204 $em->persist($entry);
203
204 $em->flush(); 205 $em->flush();
205 206
207 // entry saved, dispatch event about it!
208 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
209
206 $json = $this->get('serializer')->serialize($entry, 'json'); 210 $json = $this->get('serializer')->serialize($entry, 'json');
207 211
208 return (new JsonResponse())->setJson($json); 212 return (new JsonResponse())->setJson($json);
@@ -279,6 +283,9 @@ class EntryRestController extends WallabagRestController
279 $em->remove($entry); 283 $em->remove($entry);
280 $em->flush(); 284 $em->flush();
281 285
286 // entry deleted, dispatch event about it!
287 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
288
282 $json = $this->get('serializer')->serialize($entry, 'json'); 289 $json = $this->get('serializer')->serialize($entry, 'json');
283 290
284 return (new JsonResponse())->setJson($json); 291 return (new JsonResponse())->setJson($json);
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index e927a890..544c1ea9 100644
--- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -8,6 +8,20 @@ use Wallabag\CoreBundle\Entity\Entry;
8 8
9class WallabagRestController extends FOSRestController 9class WallabagRestController extends FOSRestController
10{ 10{
11 /**
12 * Retrieve version number.
13 *
14 * @ApiDoc()
15 *
16 * @return JsonResponse
17 */
18 public function getVersionAction()
19 {
20 $version = $this->container->getParameter('wallabag_core.version');
21 $json = $this->get('serializer')->serialize($version, 'json');
22 return (new JsonResponse())->setJson($json);
23 }
24
11 protected function validateAuthentication() 25 protected function validateAuthentication()
12 { 26 {
13 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { 27 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..8e1886ac 100644
--- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
+++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
@@ -1,9 +1,14 @@
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
11misc:
12 type: rest
13 resource: "WallabagApiBundle:WallabagRest"
9 name_prefix: api_ 14 name_prefix: api_
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index 857a8b4c..9fe90357 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
@@ -77,7 +77,7 @@ class InstallCommand extends ContainerAwareCommand
77 77
78 // testing if database driver exists 78 // testing if database driver exists
79 $fulfilled = true; 79 $fulfilled = true;
80 $label = '<comment>PDO Driver</comment>'; 80 $label = '<comment>PDO Driver (%s)</comment>';
81 $status = '<info>OK!</info>'; 81 $status = '<info>OK!</info>';
82 $help = ''; 82 $help = '';
83 83
@@ -87,7 +87,7 @@ class InstallCommand extends ContainerAwareCommand
87 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; 87 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.';
88 } 88 }
89 89
90 $rows[] = [$label, $status, $help]; 90 $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help];
91 91
92 // testing if connection to the database can be etablished 92 // testing if connection to the database can be etablished
93 $label = '<comment>Database connection</comment>'; 93 $label = '<comment>Database connection</comment>';
@@ -95,7 +95,8 @@ class InstallCommand extends ContainerAwareCommand
95 $help = ''; 95 $help = '';
96 96
97 try { 97 try {
98 $this->getContainer()->get('doctrine')->getManager()->getConnection()->connect(); 98 $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection();
99 $conn->connect();
99 } catch (\Exception $e) { 100 } catch (\Exception $e) {
100 if (false === strpos($e->getMessage(), 'Unknown database') 101 if (false === strpos($e->getMessage(), 'Unknown database')
101 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { 102 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) {
@@ -107,6 +108,21 @@ class InstallCommand extends ContainerAwareCommand
107 108
108 $rows[] = [$label, $status, $help]; 109 $rows[] = [$label, $status, $help];
109 110
111 // now check if MySQL isn't too old to handle utf8mb4
112 if ($conn->isConnected() && $conn->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) {
113 $version = $conn->query('select version()')->fetchColumn();
114 $minimalVersion = '5.5.4';
115
116 if (false === version_compare($version, $minimalVersion, '>')) {
117 $fulfilled = false;
118 $rows[] = [
119 '<comment>Database version</comment>',
120 '<error>ERROR!</error>',
121 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).',
122 ];
123 }
124 }
125
110 foreach ($this->functionExists as $functionRequired) { 126 foreach ($this->functionExists as $functionRequired) {
111 $label = '<comment>'.$functionRequired.'</comment>'; 127 $label = '<comment>'.$functionRequired.'</comment>';
112 $status = '<info>OK!</info>'; 128 $status = '<info>OK!</info>';
@@ -131,7 +147,7 @@ class InstallCommand extends ContainerAwareCommand
131 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); 147 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
132 } 148 }
133 149
134 $this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>'); 150 $this->defaultOutput->writeln('<info>Success! Your system can run wallabag properly.</info>');
135 151
136 $this->defaultOutput->writeln(''); 152 $this->defaultOutput->writeln('');
137 153
@@ -354,7 +370,7 @@ class InstallCommand extends ContainerAwareCommand
354 ], 370 ],
355 [ 371 [
356 'name' => 'wallabag_url', 372 'name' => 'wallabag_url',
357 'value' => 'http://v2.wallabag.org', 373 'value' => '',
358 'section' => 'misc', 374 'section' => 'misc',
359 ], 375 ],
360 [ 376 [
@@ -382,6 +398,11 @@ class InstallCommand extends ContainerAwareCommand
382 'value' => 'wallabag', 398 'value' => 'wallabag',
383 'section' => 'misc', 399 'section' => 'misc',
384 ], 400 ],
401 [
402 'name' => 'download_images_enabled',
403 'value' => '0',
404 'section' => 'misc',
405 ],
385 ]; 406 ];
386 407
387 foreach ($settings as $setting) { 408 foreach ($settings as $setting) {
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
index 91cdcae5..d40efcd7 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;
@@ -148,6 +149,10 @@ class ConfigController extends Controller
148 'token' => $config->getRssToken(), 149 'token' => $config->getRssToken(),
149 ], 150 ],
150 'twofactor_auth' => $this->getParameter('twofactor_auth'), 151 'twofactor_auth' => $this->getParameter('twofactor_auth'),
152 'wallabag_url' => $this->get('craue_config')->get('wallabag_url'),
153 'enabled_users' => $this->getDoctrine()
154 ->getRepository('WallabagUserBundle:User')
155 ->getSumEnabledUsers(),
151 ]); 156 ]);
152 } 157 }
153 158
@@ -221,6 +226,80 @@ class ConfigController extends Controller
221 } 226 }
222 227
223 /** 228 /**
229 * Remove all annotations OR tags OR entries for the current user.
230 *
231 * @Route("/reset/{type}", requirements={"id" = "annotations|tags|entries"}, name="config_reset")
232 *
233 * @return RedirectResponse
234 */
235 public function resetAction($type)
236 {
237 $em = $this->getDoctrine()->getManager();
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 /**
224 * 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.
225 * 304 *
226 * @param TaggingRule $rule 305 * @param TaggingRule $rule
@@ -251,4 +330,37 @@ class ConfigController extends Controller
251 330
252 return $config; 331 return $config;
253 } 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 }
254} 366}
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 97bb3d12..3f4eb17d 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -13,6 +13,8 @@ use Wallabag\CoreBundle\Form\Type\EntryFilterType;
13use Wallabag\CoreBundle\Form\Type\EditEntryType; 13use Wallabag\CoreBundle\Form\Type\EditEntryType;
14use Wallabag\CoreBundle\Form\Type\NewEntryType; 14use Wallabag\CoreBundle\Form\Type\NewEntryType;
15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; 15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
16use Wallabag\CoreBundle\Event\EntrySavedEvent;
17use Wallabag\CoreBundle\Event\EntryDeletedEvent;
16 18
17class EntryController extends Controller 19class EntryController extends Controller
18{ 20{
@@ -81,6 +83,9 @@ class EntryController extends Controller
81 $em->persist($entry); 83 $em->persist($entry);
82 $em->flush(); 84 $em->flush();
83 85
86 // entry saved, dispatch event about it!
87 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
88
84 return $this->redirect($this->generateUrl('homepage')); 89 return $this->redirect($this->generateUrl('homepage'));
85 } 90 }
86 91
@@ -107,6 +112,9 @@ class EntryController extends Controller
107 $em = $this->getDoctrine()->getManager(); 112 $em = $this->getDoctrine()->getManager();
108 $em->persist($entry); 113 $em->persist($entry);
109 $em->flush(); 114 $em->flush();
115
116 // entry saved, dispatch event about it!
117 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
110 } 118 }
111 119
112 return $this->redirect($this->generateUrl('homepage')); 120 return $this->redirect($this->generateUrl('homepage'));
@@ -343,6 +351,9 @@ class EntryController extends Controller
343 $em->persist($entry); 351 $em->persist($entry);
344 $em->flush(); 352 $em->flush();
345 353
354 // entry saved, dispatch event about it!
355 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
356
346 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()])); 357 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
347 } 358 }
348 359
@@ -431,6 +442,9 @@ class EntryController extends Controller
431 UrlGeneratorInterface::ABSOLUTE_PATH 442 UrlGeneratorInterface::ABSOLUTE_PATH
432 ); 443 );
433 444
445 // entry deleted, dispatch event about it!
446 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
447
434 $em = $this->getDoctrine()->getManager(); 448 $em = $this->getDoctrine()->getManager();
435 $em->remove($entry); 449 $em->remove($entry);
436 $em->flush(); 450 $em->flush();
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php
index 707f3bbe..a3e70fd0 100644
--- a/src/Wallabag/CoreBundle/Controller/TagController.php
+++ b/src/Wallabag/CoreBundle/Controller/TagController.php
@@ -90,15 +90,15 @@ class TagController extends Controller
90 90
91 $flatTags = []; 91 $flatTags = [];
92 92
93 foreach ($tags as $key => $tag) { 93 foreach ($tags as $tag) {
94 $nbEntries = $this->getDoctrine() 94 $nbEntries = $this->getDoctrine()
95 ->getRepository('WallabagCoreBundle:Entry') 95 ->getRepository('WallabagCoreBundle:Entry')
96 ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag['id']); 96 ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId());
97 97
98 $flatTags[] = [ 98 $flatTags[] = [
99 'id' => $tag['id'], 99 'id' => $tag->getId(),
100 'label' => $tag['label'], 100 'label' => $tag->getLabel(),
101 'slug' => $tag['slug'], 101 'slug' => $tag->getSlug(),
102 'nbEntries' => $nbEntries, 102 'nbEntries' => $nbEntries,
103 ]; 103 ];
104 } 104 }
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
index a5e1be65..d0085660 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
@@ -140,6 +140,11 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
140 'value' => 'wallabag', 140 'value' => 'wallabag',
141 'section' => 'misc', 141 'section' => 'misc',
142 ], 142 ],
143 [
144 'name' => 'download_images_enabled',
145 'value' => '0',
146 'section' => 'misc',
147 ],
143 ]; 148 ];
144 149
145 foreach ($settings as $setting) { 150 foreach ($settings as $setting) {
@@ -158,6 +163,6 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
158 */ 163 */
159 public function getOrder() 164 public function getOrder()
160 { 165 {
161 return 50; 166 return 29;
162 } 167 }
163} 168}
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index f2da3f4d..dd0f7e67 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -19,7 +19,7 @@ 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(name="`entry`", options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"})
23 * @ORM\HasLifecycleCallbacks() 23 * @ORM\HasLifecycleCallbacks()
24 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())") 24 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())")
25 */ 25 */
@@ -190,10 +190,10 @@ class Entry
190 * @ORM\JoinTable( 190 * @ORM\JoinTable(
191 * name="entry_tag", 191 * name="entry_tag",
192 * joinColumns={ 192 * joinColumns={
193 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") 193 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade")
194 * }, 194 * },
195 * inverseJoinColumns={ 195 * inverseJoinColumns={
196 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id") 196 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="cascade")
197 * } 197 * }
198 * ) 198 * )
199 */ 199 */
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..9013328f 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;
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index 8019df42..1986ab33 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;
@@ -20,7 +20,7 @@ class ContentProxy
20 protected $logger; 20 protected $logger;
21 protected $tagRepository; 21 protected $tagRepository;
22 22
23 public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, Logger $logger) 23 public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, LoggerInterface $logger)
24 { 24 {
25 $this->graby = $graby; 25 $this->graby = $graby;
26 $this->tagger = $tagger; 26 $this->tagger = $tagger;
@@ -66,6 +66,7 @@ class ContentProxy
66 $entry->setUrl($content['url'] ?: $url); 66 $entry->setUrl($content['url'] ?: $url);
67 $entry->setTitle($title); 67 $entry->setTitle($title);
68 $entry->setContent($html); 68 $entry->setContent($html);
69
69 $entry->setLanguage($content['language']); 70 $entry->setLanguage($content['language']);
70 $entry->setMimetype($content['content_type']); 71 $entry->setMimetype($content['content_type']);
71 $entry->setReadingTime(Utils::getReadingTime($html)); 72 $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..c5298236
--- /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 $result = imagegif($im, $localPath);
139 $this->logger->debug('DownloadImages: Re-creating gif');
140 break;
141 case 'jpeg':
142 case 'jpg':
143 $result = imagejpeg($im, $localPath, self::REGENERATE_PICTURES_QUALITY);
144 $this->logger->debug('DownloadImages: Re-creating jpg');
145 break;
146 case 'png':
147 $result = 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/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index cd2b47b9..14616d88 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -329,4 +329,18 @@ class EntryRepository extends EntityRepository
329 329
330 return $qb->getQuery()->getSingleScalarResult(); 330 return $qb->getQuery()->getSingleScalarResult();
331 } 331 }
332
333 /**
334 * Remove all entries for a user id.
335 * Used when a user want to reset all informations.
336 *
337 * @param int $userId
338 */
339 public function removeAllByUserId($userId)
340 {
341 $this->getEntityManager()
342 ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId')
343 ->setParameter('userId', $userId)
344 ->execute();
345 }
332} 346}
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php
index e76878d4..81445989 100644
--- a/src/Wallabag/CoreBundle/Repository/TagRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php
@@ -34,6 +34,9 @@ class TagRepository extends EntityRepository
34 34
35 /** 35 /**
36 * Find all tags per user. 36 * Find all tags per user.
37 * Instead of just left joined on the Entry table, we select only id and group by id to avoid tag multiplication in results.
38 * Once we have all tags id, we can safely request them one by one.
39 * This'll still be fastest than the previous query.
37 * 40 *
38 * @param int $userId 41 * @param int $userId
39 * 42 *
@@ -41,15 +44,20 @@ class TagRepository extends EntityRepository
41 */ 44 */
42 public function findAllTags($userId) 45 public function findAllTags($userId)
43 { 46 {
44 return $this->createQueryBuilder('t') 47 $ids = $this->createQueryBuilder('t')
45 ->select('t.slug', 't.label', 't.id') 48 ->select('t.id')
46 ->leftJoin('t.entries', 'e') 49 ->leftJoin('t.entries', 'e')
47 ->where('e.user = :userId')->setParameter('userId', $userId) 50 ->where('e.user = :userId')->setParameter('userId', $userId)
48 ->groupBy('t.slug') 51 ->groupBy('t.id')
49 ->addGroupBy('t.label')
50 ->addGroupBy('t.id')
51 ->getQuery() 52 ->getQuery()
52 ->getArrayResult(); 53 ->getArrayResult();
54
55 $tags = [];
56 foreach ($ids as $id) {
57 $tags[] = $this->find($id);
58 }
59
60 return $tags;
53 } 61 }
54 62
55 /** 63 /**
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index 90a2419e..9786ac27 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:
@@ -130,3 +130,31 @@ services:
130 arguments: 130 arguments:
131 - '@twig' 131 - '@twig'
132 - '%kernel.debug%' 132 - '%kernel.debug%'
133
134 wallabag_core.subscriber.sqlite_cascade_delete:
135 class: Wallabag\CoreBundle\Event\Subscriber\SQLiteCascadeDeleteSubscriber
136 arguments:
137 - "@doctrine"
138 tags:
139 - { name: doctrine.event_subscriber }
140
141 wallabag_core.subscriber.download_images:
142 class: Wallabag\CoreBundle\Event\Subscriber\DownloadImagesSubscriber
143 arguments:
144 - "@doctrine.orm.default_entity_manager"
145 - "@wallabag_core.entry.download_images"
146 - '@=service(''craue_config'').get(''download_images_enabled'')'
147 - "@logger"
148 tags:
149 - { name: kernel.event_subscriber }
150
151 wallabag_core.entry.download_images:
152 class: Wallabag\CoreBundle\Helper\DownloadImages
153 arguments:
154 - "@wallabag_core.entry.download_images.client"
155 - "%kernel.root_dir%/../web/assets/images"
156 - '@=service(''craue_config'').get(''wallabag_url'')'
157 - "@logger"
158
159 wallabag_core.entry.download_images.client:
160 class: GuzzleHttp\Client
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index 6ca7e459..aeae6bcf 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer 73 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'RSS-feeds fra wallabag gør det muligt at læse de artikler, der gemmes i wallabag, med din RSS-læser. Det kræver, at du genererer et token først.' 76 description: 'RSS-feeds fra wallabag gør det muligt at læse de artikler, der gemmes i wallabag, med din RSS-læser. Det kræver, at du genererer et token først.'
76 token_label: 'RSS-Token' 77 token_label: 'RSS-Token'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Navn' 89 name_label: 'Navn'
89 email_label: 'Emailadresse' 90 email_label: 'Emailadresse'
90 # twoFactorAuthentication_label: 'Two factor authentication' 91 # twoFactorAuthentication_label: 'Two factor authentication'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Gammel adgangskode' 105 old_password_label: 'Gammel adgangskode'
93 new_password_label: 'Ny adgangskode' 106 new_password_label: 'Ny adgangskode'
@@ -355,6 +368,7 @@ import:
355 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
356 worker: 369 worker:
357 # 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:" 370 # 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:"
371 # 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."
358 # firefox: 372 # firefox:
359 # page_title: 'Import > Firefox' 373 # page_title: 'Import > Firefox'
360 # 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 # 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."
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'RSS-oplysninger opdateret' 472 rss_updated: 'RSS-oplysninger opdateret'
459 # tagging_rules_updated: 'Tagging rules updated' 473 # tagging_rules_updated: 'Tagging rules updated'
460 # tagging_rules_deleted: 'Tagging rule deleted' 474 # tagging_rules_deleted: 'Tagging rule deleted'
461 # user_added: 'User "%username%" added'
462 # rss_token_updated: 'RSS token updated' 475 # rss_token_updated: 'RSS token updated'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
463 entry: 479 entry:
464 notice: 480 notice:
465 # entry_already_saved: 'Entry already saved on %date%' 481 # entry_already_saved: 'Entry already saved on %date%'
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 # client_created: 'New client created.' 506 # client_created: 'New client created.'
491 # client_deleted: 'Client deleted' 507 # client_deleted: 'Client deleted'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # 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 8fd1d82a..2105d02d 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren 73 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'Die RSS-Feeds von wallabag erlauben es dir, deine gespeicherten Artikel mit deinem bevorzugten RSS-Reader zu lesen. Vorher musst du jedoch einen Token erstellen.' 76 description: 'Die RSS-Feeds von wallabag erlauben es dir, deine gespeicherten Artikel mit deinem bevorzugten RSS-Reader zu lesen. Vorher musst du jedoch einen Token erstellen.'
76 token_label: 'RSS-Token' 77 token_label: 'RSS-Token'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Name' 89 name_label: 'Name'
89 email_label: 'E-Mail-Adresse' 90 email_label: 'E-Mail-Adresse'
90 twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' 91 twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Altes Kennwort' 105 old_password_label: 'Altes Kennwort'
93 new_password_label: 'Neues Kennwort' 106 new_password_label: 'Neues Kennwort'
@@ -355,6 +368,7 @@ import:
355 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.' 368 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.'
356 worker: 369 worker:
357 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:" 370 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:"
371 # 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."
358 firefox: 372 firefox:
359 page_title: 'Aus Firefox importieren' 373 page_title: 'Aus Firefox importieren'
360 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 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."
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'RSS-Informationen aktualisiert' 472 rss_updated: 'RSS-Informationen aktualisiert'
459 tagging_rules_updated: 'Tagging-Regeln aktualisiert' 473 tagging_rules_updated: 'Tagging-Regeln aktualisiert'
460 tagging_rules_deleted: 'Tagging-Regel gelöscht' 474 tagging_rules_deleted: 'Tagging-Regel gelöscht'
461 user_added: 'Benutzer "%username%" erstellt'
462 rss_token_updated: 'RSS-Token aktualisiert' 475 rss_token_updated: 'RSS-Token aktualisiert'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
463 entry: 479 entry:
464 notice: 480 notice:
465 entry_already_saved: 'Eintrag bereits am %date% gespeichert' 481 entry_already_saved: 'Eintrag bereits am %date% gespeichert'
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 client_created: 'Neuer Client erstellt.' 506 client_created: 'Neuer Client erstellt.'
491 client_deleted: 'Client gelöscht' 507 client_deleted: 'Client gelöscht'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index 02f56535..2bb95728 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Consumer key for Pocket to import contents 73 pocket_consumer_key_label: Consumer key for Pocket to import contents
74 android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'RSS feeds provided by wallabag allow you to read your saved articles with your favourite RSS reader. You need to generate a token first.' 76 description: 'RSS feeds provided by wallabag allow you to read your saved articles with your favourite RSS reader. You need to generate a token first.'
76 token_label: 'RSS token' 77 token_label: 'RSS token'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Name' 89 name_label: 'Name'
89 email_label: 'Email' 90 email_label: 'Email'
90 twoFactorAuthentication_label: 'Two factor authentication' 91 twoFactorAuthentication_label: 'Two factor authentication'
92 delete:
93 title: Delete my account (a.k.a danger zone)
94 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.
95 confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 button: Delete my account
97 reset:
98 title: Reset area (a.k.a danger zone)
99 description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 annotations: Remove ALL annotations
101 tags: Remove ALL tags
102 entries: Remove ALL entries
103 confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Current password' 105 old_password_label: 'Current password'
93 new_password_label: 'New password' 106 new_password_label: 'New password'
@@ -355,6 +368,7 @@ import:
355 how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 how_to: 'Please select your Readability export and click on the below button to upload and import it.'
356 worker: 369 worker:
357 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:" 370 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:"
371 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."
358 firefox: 372 firefox:
359 page_title: 'Import > Firefox' 373 page_title: 'Import > Firefox'
360 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 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."
@@ -459,6 +473,9 @@ flashes:
459 tagging_rules_updated: 'Tagging rules updated' 473 tagging_rules_updated: 'Tagging rules updated'
460 tagging_rules_deleted: 'Tagging rule deleted' 474 tagging_rules_deleted: 'Tagging rule deleted'
461 rss_token_updated: 'RSS token updated' 475 rss_token_updated: 'RSS token updated'
476 annotations_reset: Annotations reset
477 tags_reset: Tags reset
478 entries_reset: Entries reset
462 entry: 479 entry:
463 notice: 480 notice:
464 entry_already_saved: 'Entry already saved on %date%' 481 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 42ec8183..ca3db487 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -71,6 +71,7 @@ config:
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 # pocket_consumer_key_label: Consumer key for Pocket to import contents 73 # pocket_consumer_key_label: Consumer key for Pocket to import contents
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'Los feeds RSS de wallabag permiten leer los artículos guardados con su lector RSS favorito. Necesita generar un token primero' 76 description: 'Los feeds RSS de wallabag permiten leer los artículos guardados con su lector RSS favorito. Necesita generar un token primero'
76 token_label: 'RSS token' 77 token_label: 'RSS token'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Nombre' 89 name_label: 'Nombre'
89 email_label: 'Direccion e-mail' 90 email_label: 'Direccion e-mail'
90 twoFactorAuthentication_label: 'Autentificación de dos factores' 91 twoFactorAuthentication_label: 'Autentificación de dos factores'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Contraseña actual' 105 old_password_label: 'Contraseña actual'
93 new_password_label: 'Nueva contraseña' 106 new_password_label: 'Nueva contraseña'
@@ -355,6 +368,7 @@ import:
355 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
356 worker: 369 worker:
357 # 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:" 370 # 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:"
371 # 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."
358 firefox: 372 firefox:
359 page_title: 'Importar > Firefox' 373 page_title: 'Importar > Firefox'
360 # 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 # 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."
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'La configuración de los feeds RSS ha sido actualizada' 472 rss_updated: 'La configuración de los feeds RSS ha sido actualizada'
459 tagging_rules_updated: 'Regla de etiquetado borrada' 473 tagging_rules_updated: 'Regla de etiquetado borrada'
460 tagging_rules_deleted: 'Regla de etiquetado actualizada' 474 tagging_rules_deleted: 'Regla de etiquetado actualizada'
461 user_added: 'Usuario "%username%" añadido'
462 rss_token_updated: 'RSS token actualizado' 475 rss_token_updated: 'RSS token actualizado'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
463 entry: 479 entry:
464 notice: 480 notice:
465 entry_already_saved: 'Entrada ya guardada por %fecha%' 481 entry_already_saved: 'Entrada ya guardada por %fecha%'
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 client_created: 'Nuevo cliente creado.' 506 client_created: 'Nuevo cliente creado.'
491 client_deleted: 'Cliente suprimido' 507 client_deleted: 'Cliente suprimido'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # 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 f82167df..1914215a 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -71,6 +71,7 @@ config:
71 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم' 71 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم'
72 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم' 72 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم'
73 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب 73 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'با خوراک آر-اس-اس که wallabag در اختیارتان می‌گذارد، می‌توانید مقاله‌های ذخیره‌شده را در نرم‌افزار آر-اس-اس دلخواه خود بخوانید. برای این کار نخست باید یک کد بسازید.' 76 description: 'با خوراک آر-اس-اس که wallabag در اختیارتان می‌گذارد، می‌توانید مقاله‌های ذخیره‌شده را در نرم‌افزار آر-اس-اس دلخواه خود بخوانید. برای این کار نخست باید یک کد بسازید.'
76 token_label: 'کد آر-اس-اس' 77 token_label: 'کد آر-اس-اس'
@@ -88,6 +89,18 @@ config:
88 name_label: 'نام' 89 name_label: 'نام'
89 email_label: 'نشانی ایمیل' 90 email_label: 'نشانی ایمیل'
90 twoFactorAuthentication_label: 'تأیید ۲مرحله‌ای' 91 twoFactorAuthentication_label: 'تأیید ۲مرحله‌ای'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'رمز قدیمی' 105 old_password_label: 'رمز قدیمی'
93 new_password_label: 'رمز تازه' 106 new_password_label: 'رمز تازه'
@@ -272,6 +285,7 @@ quickstart:
272 paragraph_2: 'ادامه دهید!' 285 paragraph_2: 'ادامه دهید!'
273 configure: 286 configure:
274 title: 'برنامه را تنظیم کنید' 287 title: 'برنامه را تنظیم کنید'
288 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.'
275 language: 'زبان و نمای برنامه را تغییر دهید' 289 language: 'زبان و نمای برنامه را تغییر دهید'
276 rss: 'خوراک آر-اس-اس را فعال کنید' 290 rss: 'خوراک آر-اس-اس را فعال کنید'
277 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید' 291 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید'
@@ -354,6 +368,7 @@ import:
354 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
355 worker: 369 worker:
356 # 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:" 370 # 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:"
371 # 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."
357 firefox: 372 firefox:
358 page_title: 'درون‌ریزی > Firefox' 373 page_title: 'درون‌ریزی > Firefox'
359 # 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 # 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."
@@ -457,8 +472,10 @@ flashes:
457 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد' 472 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد'
458 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد' 473 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد'
459 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد' 474 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد'
460 user_added: 'کابر "%username%" افزوده شد'
461 rss_token_updated: 'کد آر-اس-اس به‌روز شد' 475 rss_token_updated: 'کد آر-اس-اس به‌روز شد'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
462 entry: 479 entry:
463 notice: 480 notice:
464 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' 481 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود'
@@ -488,3 +505,8 @@ flashes:
488 notice: 505 notice:
489 # client_created: 'New client created.' 506 # client_created: 'New client created.'
490 # client_deleted: 'Client deleted' 507 # client_deleted: 'Client deleted'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # 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 421cb8b5..60fa9a39 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données 73 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données
74 android_configuration: Configurez votre application Android
74 form_rss: 75 form_rss:
75 description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d’abord créer un jeton." 76 description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d’abord créer un jeton."
76 token_label: "Jeton RSS" 77 token_label: "Jeton RSS"
@@ -88,6 +89,18 @@ config:
88 name_label: "Nom" 89 name_label: "Nom"
89 email_label: "Adresse courriel" 90 email_label: "Adresse courriel"
90 twoFactorAuthentication_label: "Double authentification" 91 twoFactorAuthentication_label: "Double authentification"
92 delete:
93 title: Supprimer mon compte (attention danger !)
94 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é.
95 confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE)
96 button: 'Supprimer mon compte'
97 reset:
98 title: Réinitialisation (attention danger !)
99 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 !
100 annotations: Supprimer TOUTES les annotations
101 tags: Supprimer TOUS les tags
102 entries: Supprimer TOUS les articles
103 confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE)
91 form_password: 104 form_password:
92 old_password_label: "Mot de passe actuel" 105 old_password_label: "Mot de passe actuel"
93 new_password_label: "Nouveau mot de passe" 106 new_password_label: "Nouveau mot de passe"
@@ -355,6 +368,7 @@ import:
355 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." 368 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer."
356 worker: 369 worker:
357 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" 370 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :"
371 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."
358 firefox: 372 firefox:
359 page_title: "Import > Firefox" 373 page_title: "Import > Firefox"
360 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 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>"
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: "La configuration des flux RSS a bien été mise à jour" 472 rss_updated: "La configuration des flux RSS a bien été mise à jour"
459 tagging_rules_updated: "Règles mises à jour" 473 tagging_rules_updated: "Règles mises à jour"
460 tagging_rules_deleted: "Règle supprimée" 474 tagging_rules_deleted: "Règle supprimée"
461 user_added: "Utilisateur \"%username%\" ajouté"
462 rss_token_updated: "Jeton RSS mis à jour" 475 rss_token_updated: "Jeton RSS mis à jour"
476 annotations_reset: Annotations supprimées
477 tags_reset: Tags supprimés
478 entries_reset: Articles supprimés
463 entry: 479 entry:
464 notice: 480 notice:
465 entry_already_saved: "Article déjà sauvergardé le %date%" 481 entry_already_saved: "Article déjà sauvergardé le %date%"
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 client_created: "Nouveau client %name% créé" 506 client_created: "Nouveau client %name% créé"
491 client_deleted: "Client %name% supprimé" 507 client_deleted: "Client %name% supprimé"
508 user:
509 notice:
510 added: 'Utilisateur "%username%" ajouté'
511 updated: 'Utilisateur "%username%" mis à jour'
512 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 d679ef00..7f401684 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti 73 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'I feed RSS generati da wallabag ti permettono di leggere i tuoi contenuti salvati con il tuo lettore di RSS preferito. Prima, devi generare un token.' 76 description: 'I feed RSS generati da wallabag ti permettono di leggere i tuoi contenuti salvati con il tuo lettore di RSS preferito. Prima, devi generare un token.'
76 token_label: 'RSS token' 77 token_label: 'RSS token'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Nome' 89 name_label: 'Nome'
89 email_label: 'E-mail' 90 email_label: 'E-mail'
90 twoFactorAuthentication_label: 'Two factor authentication' 91 twoFactorAuthentication_label: 'Two factor authentication'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Password corrente' 105 old_password_label: 'Password corrente'
93 new_password_label: 'Nuova password' 106 new_password_label: 'Nuova password'
@@ -355,6 +368,7 @@ import:
355 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
356 worker: 369 worker:
357 # 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:" 370 # 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:"
371 # 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."
358 firefox: 372 firefox:
359 page_title: 'Importa da > Firefox' 373 page_title: 'Importa da > Firefox'
360 # 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 # 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."
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'Informazioni RSS aggiornate' 472 rss_updated: 'Informazioni RSS aggiornate'
459 tagging_rules_updated: 'Regole di tagging aggiornate' 473 tagging_rules_updated: 'Regole di tagging aggiornate'
460 tagging_rules_deleted: 'Regola di tagging aggiornate' 474 tagging_rules_deleted: 'Regola di tagging aggiornate'
461 user_added: 'Utente "%username%" aggiunto'
462 rss_token_updated: 'RSS token aggiornato' 475 rss_token_updated: 'RSS token aggiornato'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
463 entry: 479 entry:
464 notice: 480 notice:
465 entry_already_saved: 'Contenuto già salvato in data %date%' 481 entry_already_saved: 'Contenuto già salvato in data %date%'
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 client_created: 'Nuovo client creato.' 506 client_created: 'Nuovo client creato.'
491 client_deleted: 'Client eliminato' 507 client_deleted: 'Client eliminato'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # 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 af0fba0d..c3282b0e 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'
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas 73 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: "Los fluxes RSS fornits per wallabag vos permeton de legir vòstres articles salvagardats dins vòstre lector de fluxes preferit. Per los poder emplegar, vos cal, d'en primièr crear un geton." 76 description: "Los fluxes RSS fornits per wallabag vos permeton de legir vòstres articles salvagardats dins vòstre lector de fluxes preferit. Per los poder emplegar, vos cal, d'en primièr crear un geton."
76 token_label: 'Geton RSS' 77 token_label: 'Geton RSS'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Nom' 89 name_label: 'Nom'
89 email_label: 'Adreça de corrièl' 90 email_label: 'Adreça de corrièl'
90 twoFactorAuthentication_label: 'Dobla autentificacion' 91 twoFactorAuthentication_label: 'Dobla autentificacion'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Senhal actual' 105 old_password_label: 'Senhal actual'
93 new_password_label: 'Senhal novèl' 106 new_password_label: 'Senhal novèl'
@@ -96,7 +109,7 @@ config:
96 if_label: 'se' 109 if_label: 'se'
97 then_tag_as_label: 'alara atribuir las etiquetas' 110 then_tag_as_label: 'alara atribuir las etiquetas'
98 delete_rule_label: 'suprimir' 111 delete_rule_label: 'suprimir'
99 # edit_rule_label: 'edit' 112 edit_rule_label: 'modificar'
100 rule_label: 'Règla' 113 rule_label: 'Règla'
101 tags_label: 'Etiquetas' 114 tags_label: 'Etiquetas'
102 faq: 115 faq:
@@ -209,7 +222,7 @@ entry:
209 is_public_label: 'Public' 222 is_public_label: 'Public'
210 save_label: 'Enregistrar' 223 save_label: 'Enregistrar'
211 public: 224 public:
212 # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" 225 shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>"
213 226
214about: 227about:
215 page_title: 'A prepaus' 228 page_title: 'A prepaus'
@@ -265,14 +278,14 @@ howto:
265 278
266quickstart: 279quickstart:
267 page_title: 'Per ben començar' 280 page_title: 'Per ben començar'
268 # more: 'More…' 281 more: 'Mai…'
269 intro: 282 intro:
270 title: 'Benvenguda sus wallabag !' 283 title: 'Benvenguda sus wallabag !'
271 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." 284 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."
272 paragraph_2: 'Seguètz-nos ' 285 paragraph_2: 'Seguètz-nos '
273 configure: 286 configure:
274 title: "Configuratz l'aplicacio" 287 title: "Configuratz l'aplicacion"
275 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.' 288 description: "Per fin d'aver una aplicacion que vos va ben, anatz veire la configuracion de wallabag."
276 language: "Cambiatz la lenga e l'estil de l'aplicacion" 289 language: "Cambiatz la lenga e l'estil de l'aplicacion"
277 rss: 'Activatz los fluxes RSS' 290 rss: 'Activatz los fluxes RSS'
278 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' 291 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles'
@@ -286,7 +299,7 @@ quickstart:
286 import: 'Configurar los impòrt' 299 import: 'Configurar los impòrt'
287 first_steps: 300 first_steps:
288 title: 'Primièrs passes' 301 title: 'Primièrs passes'
289 # 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." 302 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."
290 new_article: 'Ajustatz vòstre primièr article' 303 new_article: 'Ajustatz vòstre primièr article'
291 unread_articles: 'E racaptatz-lo !' 304 unread_articles: 'E racaptatz-lo !'
292 migrate: 305 migrate:
@@ -298,14 +311,14 @@ quickstart:
298 readability: 'Migrar dempuèi Readability' 311 readability: 'Migrar dempuèi Readability'
299 instapaper: 'Migrar dempuèi Instapaper' 312 instapaper: 'Migrar dempuèi Instapaper'
300 developer: 313 developer:
301 title: 'Pels desvolopadors' 314 title: 'Pels desvolopaires'
302 # description: 'We also thought to the developers: Docker, API, translations, etc.' 315 description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.'
303 create_application: 'Crear vòstra aplicacion tèrça' 316 create_application: 'Crear vòstra aplicacion tèrça'
304 # use_docker: 'Use Docker to install wallabag' 317 use_docker: 'Utilizar Docker per installar wallabag'
305 docs: 318 docs:
306 title: 'Documentacion complèta' 319 title: 'Documentacion complèta'
307 # 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." 320 description: "I a un fum de fonccionalitats dins wallabag. Esitetz pas a legir lo manual per las conéisser e aprendre a las utilizar."
308 annotate: 'Anotatar vòstre article' 321 annotate: 'Anotar vòstre article'
309 export: 'Convertissètz vòstres articles en ePub o en PDF' 322 export: 'Convertissètz vòstres articles en ePub o en PDF'
310 search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" 323 search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa"
311 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" 324 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?"
@@ -355,6 +368,7 @@ import:
355 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar." 368 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar."
356 worker: 369 worker:
357 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 : " 370 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 : "
371 # 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."
358 firefox: 372 firefox:
359 page_title: 'Importar > Firefox' 373 page_title: 'Importar > Firefox'
360 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 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."
@@ -390,7 +404,7 @@ developer:
390 warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." 404 warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag."
391 action: 'Suprimir aqueste client' 405 action: 'Suprimir aqueste client'
392 client: 406 client:
393 page_title: 'Desvlopador > Novèl client' 407 page_title: 'Desvolopaire > Novèl client'
394 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." 408 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion."
395 form: 409 form:
396 name_label: "Nom del client" 410 name_label: "Nom del client"
@@ -398,7 +412,7 @@ developer:
398 save_label: 'Crear un novèl client' 412 save_label: 'Crear un novèl client'
399 action_back: 'Retorn' 413 action_back: 'Retorn'
400 client_parameter: 414 client_parameter:
401 page_title: 'Desvolopador > Los paramètres de vòstre client' 415 page_title: 'Desvolopaire > Los paramètres de vòstre client'
402 page_description: 'Vaquí los paramètres de vòstre client' 416 page_description: 'Vaquí los paramètres de vòstre client'
403 field_name: 'Nom del client' 417 field_name: 'Nom del client'
404 field_id: 'ID Client' 418 field_id: 'ID Client'
@@ -406,7 +420,7 @@ developer:
406 back: 'Retour' 420 back: 'Retour'
407 read_howto: 'Legir "cossí crear ma primièra aplicacion"' 421 read_howto: 'Legir "cossí crear ma primièra aplicacion"'
408 howto: 422 howto:
409 page_title: 'Desvolopador > Cossí crear ma primièra aplicacion' 423 page_title: 'Desvolopaire > Cossí crear ma primièra aplicacion'
410 description: 424 description:
411 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." 425 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."
412 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." 426 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar."
@@ -419,31 +433,31 @@ developer:
419 back: 'Retorn' 433 back: 'Retorn'
420 434
421user: 435user:
422 # page_title: Users management 436 page_title: 'Gestion dels utilizaires'
423 # new_user: Create a new user 437 new_user: 'Crear un novèl utilizaire'
424 # edit_user: Edit an existing user 438 edit_user: 'Modificar un utilizaire existent'
425 # description: "Here you can manage all users (create, edit and delete)" 439 description: "Aquí podètz gerir totes los utilizaires (crear, modificar e suprimir)"
426 # list: 440 list:
427 # actions: Actions 441 actions: 'Accions'
428 # edit_action: Edit 442 edit_action: 'Modificar'
429 # yes: Yes 443 yes: 'Òc'
430 # no: No 444 no: 'Non'
431 # create_new_one: Create a new user 445 create_new_one: 'Crear un novèl utilizaire'
432 form: 446 form:
433 username_label: "Nom d'utilizaire" 447 username_label: "Nom d'utilizaire"
434 # name_label: 'Name' 448 name_label: 'Nom'
435 password_label: 'Senhal' 449 password_label: 'Senhal'
436 repeat_new_password_label: 'Confirmatz vòstre novèl senhal' 450 repeat_new_password_label: 'Confirmatz vòstre novèl senhal'
437 plain_password_label: 'Senhal en clar' 451 plain_password_label: 'Senhal en clar'
438 email_label: 'Adreça de corrièl' 452 email_label: 'Adreça de corrièl'
439 # enabled_label: 'Enabled' 453 enabled_label: 'Actiu'
440 # locked_label: 'Locked' 454 locked_label: 'Varrolhat'
441 # last_login_label: 'Last login' 455 last_login_label: 'Darrièra connexion'
442 # twofactor_label: Two factor authentication 456 twofactor_label: 'Autentificacion doble-factor'
443 # save: Save 457 save: 'Enregistrar'
444 # delete: Delete 458 delete: 'Suprimir'
445 # delete_confirm: Are you sure? 459 delete_confirm: 'Sètz segur ?'
446 # back_to_list: Back to list 460 back_to_list: 'Tornar a la lista'
447 461
448error: 462error:
449 # page_title: An error occurred 463 # page_title: An error occurred
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' 472 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn'
459 tagging_rules_updated: 'Règlas misa a jorn' 473 tagging_rules_updated: 'Règlas misa a jorn'
460 tagging_rules_deleted: 'Règla suprimida' 474 tagging_rules_deleted: 'Règla suprimida'
461 user_added: 'Utilizaire "%username%" apondut'
462 rss_token_updated: 'Geton RSS mes a jorn' 475 rss_token_updated: 'Geton RSS mes a jorn'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
463 entry: 479 entry:
464 notice: 480 notice:
465 entry_already_saved: 'Article ja salvargardat lo %date%' 481 entry_already_saved: 'Article ja salvargardat lo %date%'
@@ -470,12 +486,12 @@ flashes:
470 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat" 486 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat"
471 entry_archived: 'Article marcat coma legit' 487 entry_archived: 'Article marcat coma legit'
472 entry_unarchived: 'Article marcat coma pas legit' 488 entry_unarchived: 'Article marcat coma pas legit'
473 entry_starred: 'Article apondut dins los favorits' 489 entry_starred: 'Article ajustat dins los favorits'
474 entry_unstarred: 'Article quitat dels favorits' 490 entry_unstarred: 'Article quitat dels favorits'
475 entry_deleted: 'Article suprimit' 491 entry_deleted: 'Article suprimit'
476 tag: 492 tag:
477 notice: 493 notice:
478 tag_added: 'Etiqueta aponduda' 494 tag_added: 'Etiqueta ajustada'
479 import: 495 import:
480 notice: 496 notice:
481 failed: "L'importacion a fracassat, mercés de tornar ensajar" 497 failed: "L'importacion a fracassat, mercés de tornar ensajar"
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 client_created: 'Novèl client creat' 506 client_created: 'Novèl client creat'
491 client_deleted: 'Client suprimit' 507 client_deleted: 'Client suprimit'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index bf47b58a..87731faf 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości' 73 pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości'
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'Kanały RSS prowadzone przez wallabag pozwalają Ci na czytanie twoich zapisanych artykułów w twoium ulubionym czytniku RSS. Musisz najpierw wynegenerować tokena.‌' 76 description: 'Kanały RSS prowadzone przez wallabag pozwalają Ci na czytanie twoich zapisanych artykułów w twoium ulubionym czytniku RSS. Musisz najpierw wynegenerować tokena.‌'
76 token_label: 'Token RSS' 77 token_label: 'Token RSS'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Nazwa' 89 name_label: 'Nazwa'
89 email_label: 'Adres email' 90 email_label: 'Adres email'
90 twoFactorAuthentication_label: 'Autoryzacja dwuetapowa' 91 twoFactorAuthentication_label: 'Autoryzacja dwuetapowa'
92 delete:
93 title: Usuń moje konto (niebezpieczna strefa !)
94 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.
95 confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć)
96 button: Usuń moje konto
97 reset:
98 title: Reset (niebezpieczna strefa)
99 description: Poniższe przyciski pozwalają usunąć pewne informacje z twojego konta. Uważaj te operacje są NIEODWRACALNE.
100 annotations: Usuń WSZYSTKIE adnotacje
101 tags: Usuń WSZYSTKIE tagi
102 entries: usuń WSZYTSTKIE wpisy
103 confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć)
91 form_password: 104 form_password:
92 old_password_label: 'Stare hasło' 105 old_password_label: 'Stare hasło'
93 new_password_label: 'Nowe hasło' 106 new_password_label: 'Nowe hasło'
@@ -355,6 +368,7 @@ import:
355 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.' 368 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.'
356 worker: 369 worker:
357 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:" 370 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:"
371 # 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."
358 firefox: 372 firefox:
359 page_title: 'Import > Firefox' 373 page_title: 'Import > Firefox'
360 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 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."
@@ -446,7 +460,7 @@ user:
446 back_to_list: Powrót do listy 460 back_to_list: Powrót do listy
447 461
448error: 462error:
449 # page_title: An error occurred 463 page_title: Wystąpił błąd
450 464
451flashes: 465flashes:
452 config: 466 config:
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'Informacje RSS zaktualizowane' 472 rss_updated: 'Informacje RSS zaktualizowane'
459 tagging_rules_updated: 'Reguły tagowania zaktualizowane' 473 tagging_rules_updated: 'Reguły tagowania zaktualizowane'
460 tagging_rules_deleted: 'Reguła tagowania usunięta' 474 tagging_rules_deleted: 'Reguła tagowania usunięta'
461 user_added: 'Użytkownik "%username%" dodany'
462 rss_token_updated: 'Token kanału RSS zaktualizowany' 475 rss_token_updated: 'Token kanału RSS zaktualizowany'
476 annotations_reset: Zresetuj adnotacje
477 tags_reset: Zresetuj tagi
478 entries_reset: Zresetuj wpisy
463 entry: 479 entry:
464 notice: 480 notice:
465 entry_already_saved: 'Wpis już został dodany %date%' 481 entry_already_saved: 'Wpis już został dodany %date%'
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 client_created: 'Nowy klient utworzony.' 506 client_created: 'Nowy klient utworzony.'
491 client_deleted: 'Klient usunięty' 507 client_deleted: 'Klient usunięty'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
index f10dc9aa..c1c60430 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
@@ -451,7 +451,6 @@ flashes:
451 config_saved: 'Configiração salva. Alguns parâmetros podem ser considerados depois da desconexão.' 451 config_saved: 'Configiração salva. Alguns parâmetros podem ser considerados depois da desconexão.'
452 password_updated: 'Senha atualizada' 452 password_updated: 'Senha atualizada'
453 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.' 453 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.'
454 user_updated: 'Informação atualizada'
455 rss_updated: 'Informação de RSS atualizada' 454 rss_updated: 'Informação de RSS atualizada'
456 tagging_rules_updated: 'Regras de tags atualizadas' 455 tagging_rules_updated: 'Regras de tags atualizadas'
457 tagging_rules_deleted: 'Regra de tag apagada' 456 tagging_rules_deleted: 'Regra de tag apagada'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index 875c82e8..50f1b6a2 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -71,6 +71,7 @@ config:
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 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket 73 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'Feed-urile RSS oferite de wallabag îți permit să-ți citești articolele salvate în reader-ul tău preferat RSS.' 76 description: 'Feed-urile RSS oferite de wallabag îți permit să-ți citești articolele salvate în reader-ul tău preferat RSS.'
76 token_label: 'RSS-Token' 77 token_label: 'RSS-Token'
@@ -88,6 +89,18 @@ config:
88 name_label: 'Nume' 89 name_label: 'Nume'
89 email_label: 'E-mail' 90 email_label: 'E-mail'
90 # twoFactorAuthentication_label: 'Two factor authentication' 91 # twoFactorAuthentication_label: 'Two factor authentication'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Parola veche' 105 old_password_label: 'Parola veche'
93 new_password_label: 'Parola nouă' 106 new_password_label: 'Parola nouă'
@@ -355,6 +368,7 @@ import:
355 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
356 worker: 369 worker:
357 # 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:" 370 # 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:"
371 # 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."
358 # firefox: 372 # firefox:
359 # page_title: 'Import > Firefox' 373 # page_title: 'Import > Firefox'
360 # 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 # 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."
@@ -458,8 +472,10 @@ flashes:
458 rss_updated: 'Informație RSS actualizată' 472 rss_updated: 'Informație RSS actualizată'
459 # tagging_rules_updated: 'Tagging rules updated' 473 # tagging_rules_updated: 'Tagging rules updated'
460 # tagging_rules_deleted: 'Tagging rule deleted' 474 # tagging_rules_deleted: 'Tagging rule deleted'
461 # user_added: 'User "%username%" added'
462 # rss_token_updated: 'RSS token updated' 475 # rss_token_updated: 'RSS token updated'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
463 entry: 479 entry:
464 notice: 480 notice:
465 # entry_already_saved: 'Entry already saved on %date%' 481 # entry_already_saved: 'Entry already saved on %date%'
@@ -489,3 +505,8 @@ flashes:
489 notice: 505 notice:
490 # client_created: 'New client created.' 506 # client_created: 'New client created.'
491 # client_deleted: 'Client deleted' 507 # client_deleted: 'Client deleted'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # 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 f50f629a..07939ebc 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -71,6 +71,7 @@ config:
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 # pocket_consumer_key_label: Consumer key for Pocket to import contents 73 # pocket_consumer_key_label: Consumer key for Pocket to import contents
74 # android_configuration: Configure your Android application
74 form_rss: 75 form_rss:
75 description: 'wallabag RSS akışı kaydetmiş olduğunuz makalelerini favori RSS okuyucunuzda görüntülemenizi sağlar. Bunu yapabilmek için öncelikle belirteç (token) oluşturmalısınız.' 76 description: 'wallabag RSS akışı kaydetmiş olduğunuz makalelerini favori RSS okuyucunuzda görüntülemenizi sağlar. Bunu yapabilmek için öncelikle belirteç (token) oluşturmalısınız.'
76 token_label: 'RSS belirteci (token)' 77 token_label: 'RSS belirteci (token)'
@@ -88,6 +89,18 @@ config:
88 name_label: 'İsim' 89 name_label: 'İsim'
89 email_label: 'E-posta' 90 email_label: 'E-posta'
90 twoFactorAuthentication_label: 'İki adımlı doğrulama' 91 twoFactorAuthentication_label: 'İki adımlı doğrulama'
92 delete:
93 # title: Delete my account (a.k.a danger zone)
94 # 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.
95 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
96 # button: Delete my account
97 reset:
98 # title: Reset area (a.k.a danger zone)
99 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
100 # annotations: Remove ALL annotations
101 # tags: Remove ALL tags
102 # entries: Remove ALL entries
103 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
91 form_password: 104 form_password:
92 old_password_label: 'Eski şifre' 105 old_password_label: 'Eski şifre'
93 new_password_label: 'Yeni şifre' 106 new_password_label: 'Yeni şifre'
@@ -96,6 +109,7 @@ config:
96 # if_label: 'if' 109 # if_label: 'if'
97 # then_tag_as_label: 'then tag as' 110 # then_tag_as_label: 'then tag as'
98 # delete_rule_label: 'delete' 111 # delete_rule_label: 'delete'
112 # edit_rule_label: 'edit'
99 rule_label: 'Kural' 113 rule_label: 'Kural'
100 tags_label: 'Etiketler' 114 tags_label: 'Etiketler'
101 faq: 115 faq:
@@ -354,6 +368,7 @@ import:
354 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 368 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
355 worker: 369 worker:
356 # 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:" 370 # 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:"
371 # 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."
357 firefox: 372 firefox:
358 page_title: 'İçe Aktar > Firefox' 373 page_title: 'İçe Aktar > Firefox'
359 # 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 # 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."
@@ -457,8 +472,10 @@ flashes:
457 rss_updated: 'RSS bilgiler güncellendi' 472 rss_updated: 'RSS bilgiler güncellendi'
458 tagging_rules_updated: 'Tagging rules updated' 473 tagging_rules_updated: 'Tagging rules updated'
459 tagging_rules_deleted: 'Tagging rule deleted' 474 tagging_rules_deleted: 'Tagging rule deleted'
460 user_added: 'User "%username%" added'
461 rss_token_updated: 'RSS token updated' 475 rss_token_updated: 'RSS token updated'
476 # annotations_reset: Annotations reset
477 # tags_reset: Tags reset
478 # entries_reset: Entries reset
462 entry: 479 entry:
463 notice: 480 notice:
464 entry_already_saved: 'Entry already saved on %date%' 481 entry_already_saved: 'Entry already saved on %date%'
@@ -488,3 +505,8 @@ flashes:
488 notice: 505 notice:
489 # client_created: 'New client created.' 506 # client_created: 'New client created.'
490 # client_deleted: 'Client deleted' 507 # client_deleted: 'Client deleted'
508 user:
509 notice:
510 # added: 'User "%username%" added'
511 # updated: 'User "%username%" updated'
512 # deleted: 'User "%username%" deleted'
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 ff7ef73a..ec3b23c8 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
@@ -54,6 +54,16 @@
54 <a href="https://getpocket.com/developer/docs/authentication">https://getpocket.com/developer/docs/authentication</a> 54 <a href="https://getpocket.com/developer/docs/authentication">https://getpocket.com/developer/docs/authentication</a>
55 </p> 55 </p>
56 </div> 56 </div>
57
58 <div class="row">
59 <h3>{{ 'config.form_settings.android_configuration'|trans }}</h3>
60 <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" >Touch here to prefill your Android application</a>
61 <img id="androidQrcode" />
62 <script>
63 const imgBase64 = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
64 document.getElementById('androidQrcode').src = imgBase64;
65 </script>
66 </div>
57 </fieldset> 67 </fieldset>
58 68
59 {{ form_rest(form.config) }} 69 {{ form_rest(form.config) }}
@@ -146,10 +156,41 @@
146 </fieldset> 156 </fieldset>
147 {% endif %} 157 {% endif %}
148 158
159 <h2>{{ 'config.reset.title'|trans }}</h2>
160 <fieldset class="w500p inline">
161 <p>{{ 'config.reset.description'|trans }}</p>
162 <ul>
163 <li>
164 <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
165 {{ 'config.reset.annotations'|trans }}
166 </a>
167 </li>
168 <li>
169 <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
170 {{ 'config.reset.tags'|trans }}
171 </a>
172 </li>
173 <li>
174 <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
175 {{ 'config.reset.entries'|trans }}
176 </a>
177 </li>
178 </ul>
179 </fieldset>
180
149 {{ form_widget(form.user._token) }} 181 {{ form_widget(form.user._token) }}
150 {{ form_widget(form.user.save) }} 182 {{ form_widget(form.user.save) }}
151 </form> 183 </form>
152 184
185 {% if enabled_users > 1 %}
186 <h2>{{ 'config.form_user.delete.title'|trans }}</h2>
187
188 <p>{{ 'config.form_user.delete.description'|trans }}</p>
189 <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">
190 {{ 'config.form_user.delete.button'|trans }}
191 </a>
192 {% endif %}
193
153 <h2>{{ 'config.tab_menu.password'|trans }}</h2> 194 <h2>{{ 'config.tab_menu.password'|trans }}</h2>
154 195
155 {{ form_start(form.pwd) }} 196 {{ form_start(form.pwd) }}
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 19faddc0..f69d158f 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
@@ -71,6 +71,18 @@
71 </div> 71 </div>
72 </div> 72 </div>
73 73
74 <div class="row">
75 <div class="input-field col s12">
76 <h5>{{ 'config.form_settings.android_configuration'|trans }}</h5>
77 <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>
78 <img id="androidQrcode" class="hide-on-med-and-down" />
79 </div>
80 <script>
81 const imgBase64 = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
82 document.getElementById('androidQrcode').src = imgBase64;
83 </script>
84 </div>
85
74 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 86 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
75 {{ form_rest(form.config) }} 87 {{ form_rest(form.config) }}
76 </form> 88 </form>
@@ -167,6 +179,34 @@
167 {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 179 {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
168 {{ form_widget(form.user._token) }} 180 {{ form_widget(form.user._token) }}
169 </form> 181 </form>
182
183 <br /><hr /><br />
184
185 <div class="row">
186 <h5>{{ 'config.reset.title'|trans }}</h5>
187 <p>{{ 'config.reset.description'|trans }}</p>
188 <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
189 {{ 'config.reset.annotations'|trans }}
190 </a>
191 <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
192 {{ 'config.reset.tags'|trans }}
193 </a>
194 <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
195 {{ 'config.reset.entries'|trans }}
196 </a>
197 </div>
198
199 {% if enabled_users > 1 %}
200 <br /><hr /><br />
201
202 <div class="row">
203 <h5>{{ 'config.form_user.delete.title'|trans }}</h5>
204 <p>{{ 'config.form_user.delete.description'|trans }}</p>
205 <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">
206 {{ 'config.form_user.delete.button'|trans }}
207 </a>
208 </div>
209 {% endif %}
170 </div> 210 </div>
171 211
172 <div id="set4" class="col s12"> 212 <div id="set4" class="col s12">
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php
index d1325338..13f3dcb9 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, 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,38 @@ 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');
55 break;
56 case 'instapaper':
57 $import = $this->getContainer()->get('wallabag_import.instapaper.import');
52 break; 58 break;
53 case 'instapaper': 59 case 'instapaper':
54 $wallabag = $this->getContainer()->get('wallabag_import.instapaper.import'); 60 $wallabag = $this->getContainer()->get('wallabag_import.instapaper.import');
55 break; 61 break;
56 case 'v1': 62 case 'v1':
57 default: 63 default:
58 $wallabag = $this->getContainer()->get('wallabag_import.wallabag_v1.import'); 64 $import = $this->getContainer()->get('wallabag_import.wallabag_v1.import');
59 break; 65 break;
60 } 66 }
61 67
62 $wallabag->setMarkAsRead($input->getOption('markAsRead')); 68 $import->setMarkAsRead($input->getOption('markAsRead'));
63 $wallabag->setUser($user); 69 $import->setUser($user);
64 70
65 $res = $wallabag 71 $res = $import
66 ->setFilepath($input->getArgument('filepath')) 72 ->setFilepath($input->getArgument('filepath'))
67 ->import(); 73 ->import();
68 74
69 if (true === $res) { 75 if (true === $res) {
70 $summary = $wallabag->getSummary(); 76 $summary = $import->getSummary();
71 $output->writeln('<info>'.$summary['imported'].' imported</info>'); 77 $output->writeln('<info>'.$summary['imported'].' imported</info>');
72 $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>'); 78 $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>');
73 } 79 }
diff --git a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
index b893ea29..aa7ff914 100644
--- a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
+++ b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
@@ -9,6 +9,8 @@ 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{
@@ -17,11 +19,12 @@ abstract class AbstractConsumer
17 protected $import; 19 protected $import;
18 protected $logger; 20 protected $logger;
19 21
20 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, LoggerInterface $logger = null) 22 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null)
21 { 23 {
22 $this->em = $em; 24 $this->em = $em;
23 $this->userRepository = $userRepository; 25 $this->userRepository = $userRepository;
24 $this->import = $import; 26 $this->import = $import;
27 $this->eventDispatcher = $eventDispatcher;
25 $this->logger = $logger ?: new NullLogger(); 28 $this->logger = $logger ?: new NullLogger();
26 } 29 }
27 30
@@ -59,6 +62,9 @@ abstract class AbstractConsumer
59 try { 62 try {
60 $this->em->flush(); 63 $this->em->flush();
61 64
65 // entry saved, dispatch event about it!
66 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
67
62 // clear only affected entities 68 // clear only affected entities
63 $this->em->clear(Entry::class); 69 $this->em->clear(Entry::class);
64 $this->em->clear(Tag::class); 70 $this->em->clear(Tag::class);
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/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..a5af5282 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,7 @@ 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"
23 - "@logger" 26 - "@logger"
24 wallabag_import.consumer.amqp.wallabag_v1: 27 wallabag_import.consumer.amqp.wallabag_v1:
25 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 28 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -27,6 +30,7 @@ services:
27 - "@doctrine.orm.entity_manager" 30 - "@doctrine.orm.entity_manager"
28 - "@wallabag_user.user_repository" 31 - "@wallabag_user.user_repository"
29 - "@wallabag_import.wallabag_v1.import" 32 - "@wallabag_import.wallabag_v1.import"
33 - "@event_dispatcher"
30 - "@logger" 34 - "@logger"
31 wallabag_import.consumer.amqp.wallabag_v2: 35 wallabag_import.consumer.amqp.wallabag_v2:
32 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 36 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -34,6 +38,7 @@ services:
34 - "@doctrine.orm.entity_manager" 38 - "@doctrine.orm.entity_manager"
35 - "@wallabag_user.user_repository" 39 - "@wallabag_user.user_repository"
36 - "@wallabag_import.wallabag_v2.import" 40 - "@wallabag_import.wallabag_v2.import"
41 - "@event_dispatcher"
37 - "@logger" 42 - "@logger"
38 wallabag_import.consumer.amqp.firefox: 43 wallabag_import.consumer.amqp.firefox:
39 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 44 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -41,6 +46,7 @@ services:
41 - "@doctrine.orm.entity_manager" 46 - "@doctrine.orm.entity_manager"
42 - "@wallabag_user.user_repository" 47 - "@wallabag_user.user_repository"
43 - "@wallabag_import.firefox.import" 48 - "@wallabag_import.firefox.import"
49 - "@event_dispatcher"
44 - "@logger" 50 - "@logger"
45 wallabag_import.consumer.amqp.chrome: 51 wallabag_import.consumer.amqp.chrome:
46 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 52 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -48,4 +54,5 @@ services:
48 - "@doctrine.orm.entity_manager" 54 - "@doctrine.orm.entity_manager"
49 - "@wallabag_user.user_repository" 55 - "@wallabag_user.user_repository"
50 - "@wallabag_import.chrome.import" 56 - "@wallabag_import.chrome.import"
57 - "@event_dispatcher"
51 - "@logger" 58 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/redis.yml b/src/Wallabag/ImportBundle/Resources/config/redis.yml
index 0a81e1b5..5ced4c83 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,7 @@ 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"
41 - "@logger" 43 - "@logger"
42 44
43 # pocket 45 # pocket
@@ -58,6 +60,7 @@ services:
58 - "@doctrine.orm.entity_manager" 60 - "@doctrine.orm.entity_manager"
59 - "@wallabag_user.user_repository" 61 - "@wallabag_user.user_repository"
60 - "@wallabag_import.pocket.import" 62 - "@wallabag_import.pocket.import"
63 - "@event_dispatcher"
61 - "@logger" 64 - "@logger"
62 65
63 # wallabag v1 66 # wallabag v1
@@ -78,6 +81,7 @@ services:
78 - "@doctrine.orm.entity_manager" 81 - "@doctrine.orm.entity_manager"
79 - "@wallabag_user.user_repository" 82 - "@wallabag_user.user_repository"
80 - "@wallabag_import.wallabag_v1.import" 83 - "@wallabag_import.wallabag_v1.import"
84 - "@event_dispatcher"
81 - "@logger" 85 - "@logger"
82 86
83 # wallabag v2 87 # wallabag v2
@@ -98,6 +102,7 @@ services:
98 - "@doctrine.orm.entity_manager" 102 - "@doctrine.orm.entity_manager"
99 - "@wallabag_user.user_repository" 103 - "@wallabag_user.user_repository"
100 - "@wallabag_import.wallabag_v2.import" 104 - "@wallabag_import.wallabag_v2.import"
105 - "@event_dispatcher"
101 - "@logger" 106 - "@logger"
102 107
103 # firefox 108 # firefox
@@ -118,6 +123,7 @@ services:
118 - "@doctrine.orm.entity_manager" 123 - "@doctrine.orm.entity_manager"
119 - "@wallabag_user.user_repository" 124 - "@wallabag_user.user_repository"
120 - "@wallabag_import.firefox.import" 125 - "@wallabag_import.firefox.import"
126 - "@event_dispatcher"
121 - "@logger" 127 - "@logger"
122 128
123 # chrome 129 # chrome
@@ -138,4 +144,5 @@ services:
138 - "@doctrine.orm.entity_manager" 144 - "@doctrine.orm.entity_manager"
139 - "@wallabag_user.user_repository" 145 - "@wallabag_user.user_repository"
140 - "@wallabag_import.chrome.import" 146 - "@wallabag_import.chrome.import"
147 - "@event_dispatcher"
141 - "@logger" 148 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml
index d600be0f..64822963 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,6 +65,7 @@ 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:
@@ -71,6 +76,7 @@ services:
71 arguments: 76 arguments:
72 - "@doctrine.orm.entity_manager" 77 - "@doctrine.orm.entity_manager"
73 - "@wallabag_core.content_proxy" 78 - "@wallabag_core.content_proxy"
79 - "@event_dispatcher"
74 calls: 80 calls:
75 - [ setLogger, [ "@logger" ]] 81 - [ setLogger, [ "@logger" ]]
76 tags: 82 tags:
@@ -80,6 +86,7 @@ services:
80 arguments: 86 arguments:
81 - "@doctrine.orm.entity_manager" 87 - "@doctrine.orm.entity_manager"
82 - "@wallabag_core.content_proxy" 88 - "@wallabag_core.content_proxy"
89 - "@event_dispatcher"
83 calls: 90 calls:
84 - [ setLogger, [ "@logger" ]] 91 - [ setLogger, [ "@logger" ]]
85 tags: 92 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/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"