aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorJeremy Benoist <j0k3r@users.noreply.github.com>2017-01-27 09:34:32 +0100
committerGitHub <noreply@github.com>2017-01-27 09:34:32 +0100
commit6fb06904ecde15b1b07d0a2af945338b416cf0e2 (patch)
treee76f3e8142399316ec5660fab8c646b2c34b8336 /src
parent05fa529bcfde01be5d320cb532900d72cf4b0830 (diff)
parent78295b99dd1721c613f1ce52e2debbe6f6db7753 (diff)
downloadwallabag-6fb06904ecde15b1b07d0a2af945338b416cf0e2.tar.gz
wallabag-6fb06904ecde15b1b07d0a2af945338b416cf0e2.tar.zst
wallabag-6fb06904ecde15b1b07d0a2af945338b416cf0e2.zip
Merge pull request #2416 from wallabag/2.2
wallabag 2.2.0
Diffstat (limited to 'src')
-rw-r--r--src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php74
-rw-r--r--src/Wallabag/AnnotationBundle/Entity/Annotation.php2
-rw-r--r--src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php20
-rw-r--r--src/Wallabag/ApiBundle/Controller/AnnotationRestController.php111
-rw-r--r--src/Wallabag/ApiBundle/Controller/DeveloperController.php10
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php76
-rw-r--r--src/Wallabag/ApiBundle/Controller/TagRestController.php16
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php18
-rw-r--r--src/Wallabag/ApiBundle/Entity/Client.php19
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing.yml0
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing_rest.yml18
-rw-r--r--src/Wallabag/CoreBundle/Command/ExportCommand.php77
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php56
-rw-r--r--src/Wallabag/CoreBundle/Controller/ConfigController.php141
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php73
-rw-r--r--src/Wallabag/CoreBundle/Controller/ExportController.php2
-rw-r--r--src/Wallabag/CoreBundle/Controller/RssController.php38
-rw-r--r--src/Wallabag/CoreBundle/Controller/TagController.php14
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php6
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php22
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php2
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/Configuration.php6
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php2
-rw-r--r--src/Wallabag/CoreBundle/Entity/Config.php57
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php66
-rw-r--r--src/Wallabag/CoreBundle/Entity/TaggingRule.php1
-rw-r--r--src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php26
-rw-r--r--src/Wallabag/CoreBundle/Event/EntrySavedEvent.php26
-rw-r--r--src/Wallabag/CoreBundle/Event/Listener/LocaleListener.php (renamed from src/Wallabag/CoreBundle/EventListener/LocaleListener.php)2
-rw-r--r--src/Wallabag/CoreBundle/Event/Listener/UserLocaleListener.php (renamed from src/Wallabag/CoreBundle/EventListener/UserLocaleListener.php)2
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php121
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php70
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php (renamed from src/Wallabag/CoreBundle/Subscriber/TablePrefixSubscriber.php)4
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/ConfigType.php8
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php18
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/NewTagType.php11
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/SearchEntryType.php29
-rw-r--r--src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php68
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php26
-rw-r--r--src/Wallabag/CoreBundle/Helper/DownloadImages.php233
-rw-r--r--src/Wallabag/CoreBundle/Helper/EntriesExport.php5
-rw-r--r--src/Wallabag/CoreBundle/Helper/HttpClientFactory.php54
-rw-r--r--src/Wallabag/CoreBundle/Helper/Redirect.php16
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php53
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php19
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/parameters.yml7
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml66
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml74
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml76
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml72
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml74
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml75
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml82
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml74
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml150
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml92
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml73
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml74
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml75
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/base.html.twig2
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig52
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig28
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig9
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig17
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig12
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig4
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig10
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig196
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig12
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig48
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_actions.html.twig4
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig18
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_reading_time.html.twig7
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig25
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig51
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig15
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig3
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig58
-rw-r--r--src/Wallabag/CoreBundle/Tools/Utils.php2
-rw-r--r--src/Wallabag/ImportBundle/Command/ImportCommand.php30
-rw-r--r--src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php4
-rw-r--r--src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php9
-rw-r--r--src/Wallabag/ImportBundle/Controller/BrowserController.php2
-rw-r--r--src/Wallabag/ImportBundle/Controller/ImportController.php2
-rw-r--r--src/Wallabag/ImportBundle/Controller/InstapaperController.php2
-rw-r--r--src/Wallabag/ImportBundle/Controller/PinboardController.php77
-rw-r--r--src/Wallabag/ImportBundle/Controller/ReadabilityController.php2
-rw-r--r--src/Wallabag/ImportBundle/Controller/WallabagController.php2
-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/ChromeImport.php5
-rw-r--r--src/Wallabag/ImportBundle/Import/FirefoxImport.php5
-rw-r--r--src/Wallabag/ImportBundle/Import/InstapaperImport.php3
-rw-r--r--src/Wallabag/ImportBundle/Import/PinboardImport.php141
-rw-r--r--src/Wallabag/ImportBundle/Import/PocketImport.php9
-rw-r--r--src/Wallabag/ImportBundle/Import/ReadabilityImport.php3
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV1Import.php2
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV2Import.php3
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/rabbit.yml15
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/redis.yml28
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/services.yml18
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Import/_information.html.twig (renamed from src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig)7
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig8
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig45
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig2
-rw-r--r--src/Wallabag/UserBundle/Entity/User.php26
-rw-r--r--src/Wallabag/UserBundle/EventListener/CreateConfigListener.php8
-rw-r--r--src/Wallabag/UserBundle/Form/UserType.php4
-rw-r--r--src/Wallabag/UserBundle/Repository/UserRepository.php14
-rw-r--r--src/Wallabag/UserBundle/Resources/config/services.yml4
-rw-r--r--src/Wallabag/UserBundle/Resources/translations/wallabag_user.de.yml11
-rw-r--r--src/Wallabag/UserBundle/Resources/translations/wallabag_user.en.yml2
-rw-r--r--src/Wallabag/UserBundle/Resources/views/Manage/edit.html.twig8
-rw-r--r--src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig2
-rw-r--r--src/Wallabag/UserBundle/Resources/views/Resetting/check_email.html.twig (renamed from src/Wallabag/UserBundle/Resources/views/Resetting/checkEmail.html.twig)2
-rw-r--r--src/Wallabag/UserBundle/Resources/views/TwoFactor/email_auth_code.html.twig2
121 files changed, 3462 insertions, 451 deletions
diff --git a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
index ad083e31..c13a034f 100644
--- a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
+++ b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
@@ -3,9 +3,8 @@
3namespace Wallabag\AnnotationBundle\Controller; 3namespace Wallabag\AnnotationBundle\Controller;
4 4
5use FOS\RestBundle\Controller\FOSRestController; 5use FOS\RestBundle\Controller\FOSRestController;
6use Nelmio\ApiDocBundle\Annotation\ApiDoc; 6use Symfony\Component\HttpFoundation\JsonResponse;
7use Symfony\Component\HttpFoundation\Request; 7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpFoundation\Response;
9use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; 8use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
10use Wallabag\AnnotationBundle\Entity\Annotation; 9use Wallabag\AnnotationBundle\Entity\Annotation;
11use Wallabag\CoreBundle\Entity\Entry; 10use Wallabag\CoreBundle\Entity\Entry;
@@ -15,42 +14,35 @@ class WallabagAnnotationController extends FOSRestController
15 /** 14 /**
16 * Retrieve annotations for an entry. 15 * Retrieve annotations for an entry.
17 * 16 *
18 * @ApiDoc( 17 * @param Entry $entry
19 * requirements={ 18 *
20 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} 19 * @see Wallabag\ApiBundle\Controller\WallabagRestController
21 * }
22 * )
23 * 20 *
24 * @return Response 21 * @return JsonResponse
25 */ 22 */
26 public function getAnnotationsAction(Entry $entry) 23 public function getAnnotationsAction(Entry $entry)
27 { 24 {
28 $annotationRows = $this 25 $annotationRows = $this
29 ->getDoctrine() 26 ->getDoctrine()
30 ->getRepository('WallabagAnnotationBundle:Annotation') 27 ->getRepository('WallabagAnnotationBundle:Annotation')
31 ->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId()); 28 ->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId());
32 $total = count($annotationRows); 29 $total = count($annotationRows);
33 $annotations = ['total' => $total, 'rows' => $annotationRows]; 30 $annotations = ['total' => $total, 'rows' => $annotationRows];
34 31
35 $json = $this->get('serializer')->serialize($annotations, 'json'); 32 $json = $this->get('serializer')->serialize($annotations, 'json');
36 33
37 return $this->renderJsonResponse($json); 34 return (new JsonResponse())->setJson($json);
38 } 35 }
39 36
40 /** 37 /**
41 * Creates a new annotation. 38 * Creates a new annotation.
42 * 39 *
43 * @param Entry $entry 40 * @param Request $request
41 * @param Entry $entry
44 * 42 *
45 * @ApiDoc( 43 * @return JsonResponse
46 * requirements={
47 * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"},
48 * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"},
49 * {"name"="text", "dataType"="string", "required"=true, "description"=""},
50 * }
51 * )
52 * 44 *
53 * @return Response 45 * @see Wallabag\ApiBundle\Controller\WallabagRestController
54 */ 46 */
55 public function postAnnotationAction(Request $request, Entry $entry) 47 public function postAnnotationAction(Request $request, Entry $entry)
56 { 48 {
@@ -75,21 +67,20 @@ class WallabagAnnotationController extends FOSRestController
75 67
76 $json = $this->get('serializer')->serialize($annotation, 'json'); 68 $json = $this->get('serializer')->serialize($annotation, 'json');
77 69
78 return $this->renderJsonResponse($json); 70 return (new JsonResponse())->setJson($json);
79 } 71 }
80 72
81 /** 73 /**
82 * Updates an annotation. 74 * Updates an annotation.
83 * 75 *
84 * @ApiDoc( 76 * @see Wallabag\ApiBundle\Controller\WallabagRestController
85 * requirements={
86 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
87 * }
88 * )
89 * 77 *
90 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") 78 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
91 * 79 *
92 * @return Response 80 * @param Annotation $annotation
81 * @param Request $request
82 *
83 * @return JsonResponse
93 */ 84 */
94 public function putAnnotationAction(Annotation $annotation, Request $request) 85 public function putAnnotationAction(Annotation $annotation, Request $request)
95 { 86 {
@@ -104,21 +95,19 @@ class WallabagAnnotationController extends FOSRestController
104 95
105 $json = $this->get('serializer')->serialize($annotation, 'json'); 96 $json = $this->get('serializer')->serialize($annotation, 'json');
106 97
107 return $this->renderJsonResponse($json); 98 return (new JsonResponse())->setJson($json);
108 } 99 }
109 100
110 /** 101 /**
111 * Removes an annotation. 102 * Removes an annotation.
112 * 103 *
113 * @ApiDoc( 104 * @see Wallabag\ApiBundle\Controller\WallabagRestController
114 * requirements={
115 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
116 * }
117 * )
118 * 105 *
119 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") 106 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
120 * 107 *
121 * @return Response 108 * @param Annotation $annotation
109 *
110 * @return JsonResponse
122 */ 111 */
123 public function deleteAnnotationAction(Annotation $annotation) 112 public function deleteAnnotationAction(Annotation $annotation)
124 { 113 {
@@ -128,19 +117,6 @@ class WallabagAnnotationController extends FOSRestController
128 117
129 $json = $this->get('serializer')->serialize($annotation, 'json'); 118 $json = $this->get('serializer')->serialize($annotation, 'json');
130 119
131 return $this->renderJsonResponse($json); 120 return (new JsonResponse())->setJson($json);
132 }
133
134 /**
135 * Send a JSON Response.
136 * We don't use the Symfony JsonRespone, because it takes an array as parameter instead of a JSON string.
137 *
138 * @param string $json
139 *
140 * @return Response
141 */
142 private function renderJsonResponse($json, $code = 200)
143 {
144 return new Response($json, $code, ['application/json']);
145 } 121 }
146} 122}
diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php
index c48d8731..0838f5aa 100644
--- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php
+++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php
@@ -82,7 +82,7 @@ class Annotation
82 * @Exclude 82 * @Exclude
83 * 83 *
84 * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="annotations") 84 * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="annotations")
85 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") 85 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade")
86 */ 86 */
87 private $entry; 87 private $entry;
88 88
diff --git a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php
index 5f7da70e..8d3f07ee 100644
--- a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php
+++ b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php
@@ -50,7 +50,8 @@ class AnnotationRepository extends EntityRepository
50 { 50 {
51 return $this->createQueryBuilder('a') 51 return $this->createQueryBuilder('a')
52 ->andWhere('a.id = :annotationId')->setParameter('annotationId', $annotationId) 52 ->andWhere('a.id = :annotationId')->setParameter('annotationId', $annotationId)
53 ->getQuery()->getSingleResult() 53 ->getQuery()
54 ->getSingleResult()
54 ; 55 ;
55 } 56 }
56 57
@@ -67,7 +68,8 @@ class AnnotationRepository extends EntityRepository
67 return $this->createQueryBuilder('a') 68 return $this->createQueryBuilder('a')
68 ->where('a.entry = :entryId')->setParameter('entryId', $entryId) 69 ->where('a.entry = :entryId')->setParameter('entryId', $entryId)
69 ->andwhere('a.user = :userId')->setParameter('userId', $userId) 70 ->andwhere('a.user = :userId')->setParameter('userId', $userId)
70 ->getQuery()->getResult() 71 ->getQuery()
72 ->getResult()
71 ; 73 ;
72 } 74 }
73 75
@@ -106,4 +108,18 @@ class AnnotationRepository extends EntityRepository
106 ->getQuery() 108 ->getQuery()
107 ->getSingleResult(); 109 ->getSingleResult();
108 } 110 }
111
112 /**
113 * Remove all annotations for a user id.
114 * Used when a user want to reset all informations.
115 *
116 * @param int $userId
117 */
118 public function removeAllByUserId($userId)
119 {
120 $this->getEntityManager()
121 ->createQuery('DELETE FROM Wallabag\AnnotationBundle\Entity\Annotation a WHERE a.user = :userId')
122 ->setParameter('userId', $userId)
123 ->execute();
124 }
109} 125}
diff --git a/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php
new file mode 100644
index 00000000..2dd26c07
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php
@@ -0,0 +1,111 @@
1<?php
2
3namespace Wallabag\ApiBundle\Controller;
4
5use Nelmio\ApiDocBundle\Annotation\ApiDoc;
6use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpFoundation\JsonResponse;
9use Wallabag\CoreBundle\Entity\Entry;
10use Wallabag\AnnotationBundle\Entity\Annotation;
11
12class AnnotationRestController extends WallabagRestController
13{
14 /**
15 * Retrieve annotations for an entry.
16 *
17 * @ApiDoc(
18 * requirements={
19 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
20 * }
21 * )
22 *
23 * @param Entry $entry
24 *
25 * @return JsonResponse
26 */
27 public function getAnnotationsAction(Entry $entry)
28 {
29 $this->validateAuthentication();
30
31 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:getAnnotations', [
32 'entry' => $entry,
33 ]);
34 }
35
36 /**
37 * Creates a new annotation.
38 *
39 * @ApiDoc(
40 * requirements={
41 * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"},
42 * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"},
43 * {"name"="text", "dataType"="string", "required"=true, "description"=""},
44 * }
45 * )
46 *
47 * @param Request $request
48 * @param Entry $entry
49 *
50 * @return JsonResponse
51 */
52 public function postAnnotationAction(Request $request, Entry $entry)
53 {
54 $this->validateAuthentication();
55
56 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:postAnnotation', [
57 'request' => $request,
58 'entry' => $entry,
59 ]);
60 }
61
62 /**
63 * Updates an annotation.
64 *
65 * @ApiDoc(
66 * requirements={
67 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
68 * }
69 * )
70 *
71 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
72 *
73 * @param Annotation $annotation
74 * @param Request $request
75 *
76 * @return JsonResponse
77 */
78 public function putAnnotationAction(Annotation $annotation, Request $request)
79 {
80 $this->validateAuthentication();
81
82 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:putAnnotation', [
83 'annotation' => $annotation,
84 'request' => $request,
85 ]);
86 }
87
88 /**
89 * Removes an annotation.
90 *
91 * @ApiDoc(
92 * requirements={
93 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
94 * }
95 * )
96 *
97 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
98 *
99 * @param Annotation $annotation
100 *
101 * @return JsonResponse
102 */
103 public function deleteAnnotationAction(Annotation $annotation)
104 {
105 $this->validateAuthentication();
106
107 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:deleteAnnotation', [
108 'annotation' => $annotation,
109 ]);
110 }
111}
diff --git a/src/Wallabag/ApiBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php
index 5a36a260..9cb1b626 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,11 +38,11 @@ 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
45 if ($clientForm->isValid()) { 45 if ($clientForm->isSubmitted() && $clientForm->isValid()) {
46 $client->setAllowedGrantTypes(['token', 'authorization_code', 'password', 'refresh_token']); 46 $client->setAllowedGrantTypes(['token', 'authorization_code', 'password', 'refresh_token']);
47 $em->persist($client); 47 $em->persist($client);
48 $em->flush(); 48 $em->flush();
@@ -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..2c2ec0c1 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -10,6 +10,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
10use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 10use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
11use Wallabag\CoreBundle\Entity\Entry; 11use Wallabag\CoreBundle\Entity\Entry;
12use Wallabag\CoreBundle\Entity\Tag; 12use Wallabag\CoreBundle\Entity\Tag;
13use Wallabag\CoreBundle\Event\EntrySavedEvent;
14use Wallabag\CoreBundle\Event\EntryDeletedEvent;
13 15
14class EntryRestController extends WallabagRestController 16class EntryRestController extends WallabagRestController
15{ 17{
@@ -149,6 +151,28 @@ class EntryRestController extends WallabagRestController
149 } 151 }
150 152
151 /** 153 /**
154 * Retrieve a single entry as a predefined format.
155 *
156 * @ApiDoc(
157 * requirements={
158 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
159 * }
160 * )
161 *
162 * @return Response
163 */
164 public function getEntryExportAction(Entry $entry, Request $request)
165 {
166 $this->validateAuthentication();
167 $this->validateUserAccess($entry->getUser()->getId());
168
169 return $this->get('wallabag_core.helper.entries_export')
170 ->setEntries($entry)
171 ->updateTitle('entry')
172 ->exportAs($request->attributes->get('_format'));
173 }
174
175 /**
152 * Create an entry. 176 * Create an entry.
153 * 177 *
154 * @ApiDoc( 178 * @ApiDoc(
@@ -200,9 +224,11 @@ class EntryRestController extends WallabagRestController
200 224
201 $em = $this->getDoctrine()->getManager(); 225 $em = $this->getDoctrine()->getManager();
202 $em->persist($entry); 226 $em->persist($entry);
203
204 $em->flush(); 227 $em->flush();
205 228
229 // entry saved, dispatch event about it!
230 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
231
206 $json = $this->get('serializer')->serialize($entry, 'json'); 232 $json = $this->get('serializer')->serialize($entry, 'json');
207 233
208 return (new JsonResponse())->setJson($json); 234 return (new JsonResponse())->setJson($json);
@@ -260,6 +286,51 @@ class EntryRestController extends WallabagRestController
260 } 286 }
261 287
262 /** 288 /**
289 * Reload an entry.
290 * An empty response with HTTP Status 304 will be send if we weren't able to update the content (because it hasn't changed or we got an error).
291 *
292 * @ApiDoc(
293 * requirements={
294 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
295 * }
296 * )
297 *
298 * @return JsonResponse
299 */
300 public function patchEntriesReloadAction(Entry $entry)
301 {
302 $this->validateAuthentication();
303 $this->validateUserAccess($entry->getUser()->getId());
304
305 try {
306 $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
307 } catch (\Exception $e) {
308 $this->get('logger')->error('Error while saving an entry', [
309 'exception' => $e,
310 'entry' => $entry,
311 ]);
312
313 return new JsonResponse([], 304);
314 }
315
316 // if refreshing entry failed, don't save it
317 if ($this->getParameter('wallabag_core.fetching_error_message') === $entry->getContent()) {
318 return new JsonResponse([], 304);
319 }
320
321 $em = $this->getDoctrine()->getManager();
322 $em->persist($entry);
323 $em->flush();
324
325 // entry saved, dispatch event about it!
326 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
327
328 $json = $this->get('serializer')->serialize($entry, 'json');
329
330 return (new JsonResponse())->setJson($json);
331 }
332
333 /**
263 * Delete **permanently** an entry. 334 * Delete **permanently** an entry.
264 * 335 *
265 * @ApiDoc( 336 * @ApiDoc(
@@ -279,6 +350,9 @@ class EntryRestController extends WallabagRestController
279 $em->remove($entry); 350 $em->remove($entry);
280 $em->flush(); 351 $em->flush();
281 352
353 // entry deleted, dispatch event about it!
354 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
355
282 $json = $this->get('serializer')->serialize($entry, 'json'); 356 $json = $this->get('serializer')->serialize($entry, 'json');
283 357
284 return (new JsonResponse())->setJson($json); 358 return (new JsonResponse())->setJson($json);
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php
index 4e7ddc66..bc6d4e64 100644
--- a/src/Wallabag/ApiBundle/Controller/TagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php
@@ -132,22 +132,6 @@ class TagRestController extends WallabagRestController
132 } 132 }
133 133
134 /** 134 /**
135 * Retrieve version number.
136 *
137 * @ApiDoc()
138 *
139 * @return JsonResponse
140 */
141 public function getVersionAction()
142 {
143 $version = $this->container->getParameter('wallabag_core.version');
144
145 $json = $this->get('serializer')->serialize($version, 'json');
146
147 return (new JsonResponse())->setJson($json);
148 }
149
150 /**
151 * Remove orphan tag in case no entries are associated to it. 135 * Remove orphan tag in case no entries are associated to it.
152 * 136 *
153 * @param Tag|array $tags 137 * @param Tag|array $tags
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index e927a890..b1e08ca4 100644
--- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -3,11 +3,27 @@
3namespace Wallabag\ApiBundle\Controller; 3namespace Wallabag\ApiBundle\Controller;
4 4
5use FOS\RestBundle\Controller\FOSRestController; 5use FOS\RestBundle\Controller\FOSRestController;
6use Nelmio\ApiDocBundle\Annotation\ApiDoc;
7use Symfony\Component\HttpFoundation\JsonResponse;
6use Symfony\Component\Security\Core\Exception\AccessDeniedException; 8use Symfony\Component\Security\Core\Exception\AccessDeniedException;
7use Wallabag\CoreBundle\Entity\Entry;
8 9
9class WallabagRestController extends FOSRestController 10class WallabagRestController extends FOSRestController
10{ 11{
12 /**
13 * Retrieve version number.
14 *
15 * @ApiDoc()
16 *
17 * @return JsonResponse
18 */
19 public function getVersionAction()
20 {
21 $version = $this->container->getParameter('wallabag_core.version');
22 $json = $this->get('serializer')->serialize($version, 'json');
23
24 return (new JsonResponse())->setJson($json);
25 }
26
11 protected function validateAuthentication() 27 protected function validateAuthentication()
12 { 28 {
13 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { 29 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php
index f7898ac8..9ed9f980 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")
@@ -21,7 +22,7 @@ class Client extends BaseClient
21 /** 22 /**
22 * @var string 23 * @var string
23 * 24 *
24 * @ORM\Column(name="name", type="text", nullable=true) 25 * @ORM\Column(name="name", type="text", nullable=false)
25 */ 26 */
26 protected $name; 27 protected $name;
27 28
@@ -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.yml b/src/Wallabag/ApiBundle/Resources/config/routing.yml
deleted file mode 100644
index e69de29b..00000000
--- a/src/Wallabag/ApiBundle/Resources/config/routing.yml
+++ /dev/null
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
index c1af9e02..57d37f4b 100644
--- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
+++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
@@ -1,9 +1,19 @@
1entries: 1entry:
2 type: rest 2 type: rest
3 resource: "WallabagApiBundle:EntryRest" 3 resource: "WallabagApiBundle:EntryRest"
4 name_prefix: api_ 4 name_prefix: api_
5 5
6tags: 6tag:
7 type: rest 7 type: rest
8 resource: "WallabagApiBundle:TagRest" 8 resource: "WallabagApiBundle:TagRest"
9 name_prefix: api_
10
11annotation:
12 type: rest
13 resource: "WallabagApiBundle:AnnotationRest"
14 name_prefix: api_
15
16misc:
17 type: rest
18 resource: "WallabagApiBundle:WallabagRest"
9 name_prefix: api_ 19 name_prefix: api_
diff --git a/src/Wallabag/CoreBundle/Command/ExportCommand.php b/src/Wallabag/CoreBundle/Command/ExportCommand.php
new file mode 100644
index 00000000..e3d3b399
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Command/ExportCommand.php
@@ -0,0 +1,77 @@
1<?php
2
3namespace Wallabag\CoreBundle\Command;
4
5use Doctrine\ORM\NoResultException;
6use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface;
10
11class ExportCommand extends ContainerAwareCommand
12{
13 protected function configure()
14 {
15 $this
16 ->setName('wallabag:export')
17 ->setDescription('Export all entries for an user')
18 ->setHelp('This command helps you to export all entries for an user')
19 ->addArgument(
20 'username',
21 InputArgument::REQUIRED,
22 'User from which to export entries'
23 )
24 ->addArgument(
25 'filepath',
26 InputArgument::OPTIONAL,
27 'Path of the exported file'
28 )
29 ;
30 }
31
32 protected function execute(InputInterface $input, OutputInterface $output)
33 {
34 try {
35 $user = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($input->getArgument('username'));
36 } catch (NoResultException $e) {
37 $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username')));
38
39 return 1;
40 }
41
42 $entries = $this->getDoctrine()
43 ->getRepository('WallabagCoreBundle:Entry')
44 ->getBuilderForAllByUser($user->getId())
45 ->getQuery()
46 ->getResult();
47
48 $output->write(sprintf('Exporting %d entrie(s) for user « <comment>%s</comment> »... ', count($entries), $user->getUserName()));
49
50 $filePath = $input->getArgument('filepath');
51
52 if (!$filePath) {
53 $filePath = $this->getContainer()->getParameter('kernel.root_dir').'/../'.sprintf('%s-export.json', $user->getUsername());
54 }
55
56 try {
57 $data = $this->getContainer()->get('wallabag_core.helper.entries_export')
58 ->setEntries($entries)
59 ->updateTitle('All')
60 ->exportJsonData();
61 file_put_contents($filePath, $data);
62 } catch (\InvalidArgumentException $e) {
63 $output->writeln(sprintf('<error>Error: "%s"</error>', $e->getMessage()));
64
65 return 1;
66 }
67
68 $output->writeln('<info>Done.</info>');
69
70 return 0;
71 }
72
73 private function getDoctrine()
74 {
75 return $this->getContainer()->get('doctrine');
76 }
77}
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index 2daf2dd8..f0738b91 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -40,7 +40,7 @@ class InstallCommand extends ContainerAwareCommand
40 { 40 {
41 $this 41 $this
42 ->setName('wallabag:install') 42 ->setName('wallabag:install')
43 ->setDescription('Wallabag installer.') 43 ->setDescription('wallabag installer.')
44 ->addOption( 44 ->addOption(
45 'reset', 45 'reset',
46 null, 46 null,
@@ -55,7 +55,7 @@ class InstallCommand extends ContainerAwareCommand
55 $this->defaultInput = $input; 55 $this->defaultInput = $input;
56 $this->defaultOutput = $output; 56 $this->defaultOutput = $output;
57 57
58 $output->writeln('<info>Installing Wallabag...</info>'); 58 $output->writeln('<info>Installing wallabag...</info>');
59 $output->writeln(''); 59 $output->writeln('');
60 60
61 $this 61 $this
@@ -65,7 +65,7 @@ class InstallCommand extends ContainerAwareCommand
65 ->setupConfig() 65 ->setupConfig()
66 ; 66 ;
67 67
68 $output->writeln('<info>Wallabag has been successfully installed.</info>'); 68 $output->writeln('<info>wallabag has been successfully installed.</info>');
69 $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>'); 69 $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>');
70 } 70 }
71 71
@@ -78,7 +78,7 @@ class InstallCommand extends ContainerAwareCommand
78 78
79 // testing if database driver exists 79 // testing if database driver exists
80 $fulfilled = true; 80 $fulfilled = true;
81 $label = '<comment>PDO Driver</comment>'; 81 $label = '<comment>PDO Driver (%s)</comment>';
82 $status = '<info>OK!</info>'; 82 $status = '<info>OK!</info>';
83 $help = ''; 83 $help = '';
84 84
@@ -88,7 +88,7 @@ class InstallCommand extends ContainerAwareCommand
88 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; 88 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.';
89 } 89 }
90 90
91 $rows[] = [$label, $status, $help]; 91 $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help];
92 92
93 // testing if connection to the database can be etablished 93 // testing if connection to the database can be etablished
94 $label = '<comment>Database connection</comment>'; 94 $label = '<comment>Database connection</comment>';
@@ -96,7 +96,8 @@ class InstallCommand extends ContainerAwareCommand
96 $help = ''; 96 $help = '';
97 97
98 try { 98 try {
99 $doctrineManager->getConnection()->connect(); 99 $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection();
100 $conn->connect();
100 } catch (\Exception $e) { 101 } catch (\Exception $e) {
101 if (false === strpos($e->getMessage(), 'Unknown database') 102 if (false === strpos($e->getMessage(), 'Unknown database')
102 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { 103 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) {
@@ -108,12 +109,25 @@ class InstallCommand extends ContainerAwareCommand
108 109
109 $rows[] = [$label, $status, $help]; 110 $rows[] = [$label, $status, $help];
110 111
111 // testing if PostgreSQL > 9.1 112 // check MySQL & PostgreSQL version
112 $label = '<comment>SGBD version</comment>'; 113 $label = '<comment>Database version</comment>';
113 $status = '<info>OK!</info>'; 114 $status = '<info>OK!</info>';
114 $help = ''; 115 $help = '';
115 116
116 if ('postgresql' === $doctrineManager->getConnection()->getSchemaManager()->getDatabasePlatform()->getName()) { 117 // now check if MySQL isn't too old to handle utf8mb4
118 if ($conn->isConnected() && 'mysql' === $conn->getDatabasePlatform()->getName()) {
119 $version = $conn->query('select version()')->fetchColumn();
120 $minimalVersion = '5.5.4';
121
122 if (false === version_compare($version, $minimalVersion, '>')) {
123 $fulfilled = false;
124 $status = '<error>ERROR!</error>';
125 $help = 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).';
126 }
127 }
128
129 // testing if PostgreSQL > 9.1
130 if ($conn->isConnected() && 'postgresql' === $conn->getDatabasePlatform()->getName()) {
117 // return version should be like "PostgreSQL 9.5.4 on x86_64-apple-darwin15.6.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.38), 64-bit" 131 // return version should be like "PostgreSQL 9.5.4 on x86_64-apple-darwin15.6.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.38), 64-bit"
118 $version = $doctrineManager->getConnection()->query('SELECT version();')->fetchColumn(); 132 $version = $doctrineManager->getConnection()->query('SELECT version();')->fetchColumn();
119 133
@@ -152,7 +166,7 @@ class InstallCommand extends ContainerAwareCommand
152 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); 166 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
153 } 167 }
154 168
155 $this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>'); 169 $this->defaultOutput->writeln('<info>Success! Your system can run wallabag properly.</info>');
156 170
157 $this->defaultOutput->writeln(''); 171 $this->defaultOutput->writeln('');
158 172
@@ -299,6 +313,16 @@ class InstallCommand extends ContainerAwareCommand
299 'section' => 'entry', 313 'section' => 'entry',
300 ], 314 ],
301 [ 315 [
316 'name' => 'share_unmark',
317 'value' => '1',
318 'section' => 'entry',
319 ],
320 [
321 'name' => 'unmark_url',
322 'value' => 'https://unmark.it',
323 'section' => 'entry',
324 ],
325 [
302 'name' => 'share_shaarli', 326 'name' => 'share_shaarli',
303 'value' => '1', 327 'value' => '1',
304 'section' => 'entry', 328 'section' => 'entry',
@@ -375,7 +399,7 @@ class InstallCommand extends ContainerAwareCommand
375 ], 399 ],
376 [ 400 [
377 'name' => 'wallabag_url', 401 'name' => 'wallabag_url',
378 'value' => 'http://v2.wallabag.org', 402 'value' => '',
379 'section' => 'misc', 403 'section' => 'misc',
380 ], 404 ],
381 [ 405 [
@@ -403,6 +427,16 @@ class InstallCommand extends ContainerAwareCommand
403 'value' => 'wallabag', 427 'value' => 'wallabag',
404 'section' => 'misc', 428 'section' => 'misc',
405 ], 429 ],
430 [
431 'name' => 'download_images_enabled',
432 'value' => '0',
433 'section' => 'misc',
434 ],
435 [
436 'name' => 'restricted_access',
437 'value' => '0',
438 'section' => 'entry',
439 ],
406 ]; 440 ];
407 441
408 foreach ($settings as $setting) { 442 foreach ($settings as $setting) {
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
index 46fb9503..907bf78e 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;
@@ -34,7 +35,7 @@ class ConfigController extends Controller
34 $configForm = $this->createForm(ConfigType::class, $config, ['action' => $this->generateUrl('config')]); 35 $configForm = $this->createForm(ConfigType::class, $config, ['action' => $this->generateUrl('config')]);
35 $configForm->handleRequest($request); 36 $configForm->handleRequest($request);
36 37
37 if ($configForm->isValid()) { 38 if ($configForm->isSubmitted() && $configForm->isValid()) {
38 $em->persist($config); 39 $em->persist($config);
39 $em->flush(); 40 $em->flush();
40 41
@@ -56,7 +57,7 @@ class ConfigController extends Controller
56 $pwdForm = $this->createForm(ChangePasswordType::class, null, ['action' => $this->generateUrl('config').'#set4']); 57 $pwdForm = $this->createForm(ChangePasswordType::class, null, ['action' => $this->generateUrl('config').'#set4']);
57 $pwdForm->handleRequest($request); 58 $pwdForm->handleRequest($request);
58 59
59 if ($pwdForm->isValid()) { 60 if ($pwdForm->isSubmitted() && $pwdForm->isValid()) {
60 if ($this->get('craue_config')->get('demo_mode_enabled') && $this->get('craue_config')->get('demo_mode_username') === $user->getUsername()) { 61 if ($this->get('craue_config')->get('demo_mode_enabled') && $this->get('craue_config')->get('demo_mode_username') === $user->getUsername()) {
61 $message = 'flashes.config.notice.password_not_updated_demo'; 62 $message = 'flashes.config.notice.password_not_updated_demo';
62 } else { 63 } else {
@@ -78,7 +79,7 @@ class ConfigController extends Controller
78 ]); 79 ]);
79 $userForm->handleRequest($request); 80 $userForm->handleRequest($request);
80 81
81 if ($userForm->isValid()) { 82 if ($userForm->isSubmitted() && $userForm->isValid()) {
82 $userManager->updateUser($user, true); 83 $userManager->updateUser($user, true);
83 84
84 $this->get('session')->getFlashBag()->add( 85 $this->get('session')->getFlashBag()->add(
@@ -93,7 +94,7 @@ class ConfigController extends Controller
93 $rssForm = $this->createForm(RssType::class, $config, ['action' => $this->generateUrl('config').'#set2']); 94 $rssForm = $this->createForm(RssType::class, $config, ['action' => $this->generateUrl('config').'#set2']);
94 $rssForm->handleRequest($request); 95 $rssForm->handleRequest($request);
95 96
96 if ($rssForm->isValid()) { 97 if ($rssForm->isSubmitted() && $rssForm->isValid()) {
97 $em->persist($config); 98 $em->persist($config);
98 $em->flush(); 99 $em->flush();
99 100
@@ -124,7 +125,7 @@ class ConfigController extends Controller
124 $newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, ['action' => $action]); 125 $newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, ['action' => $action]);
125 $newTaggingRule->handleRequest($request); 126 $newTaggingRule->handleRequest($request);
126 127
127 if ($newTaggingRule->isValid()) { 128 if ($newTaggingRule->isSubmitted() && $newTaggingRule->isValid()) {
128 $taggingRule->setConfig($config); 129 $taggingRule->setConfig($config);
129 $em->persist($taggingRule); 130 $em->persist($taggingRule);
130 $em->flush(); 131 $em->flush();
@@ -150,6 +151,10 @@ class ConfigController extends Controller
150 'token' => $config->getRssToken(), 151 'token' => $config->getRssToken(),
151 ], 152 ],
152 'twofactor_auth' => $this->getParameter('twofactor_auth'), 153 'twofactor_auth' => $this->getParameter('twofactor_auth'),
154 'wallabag_url' => $this->get('craue_config')->get('wallabag_url'),
155 'enabled_users' => $this->getDoctrine()
156 ->getRepository('WallabagUserBundle:User')
157 ->getSumEnabledUsers(),
153 ]); 158 ]);
154 } 159 }
155 160
@@ -223,6 +228,78 @@ class ConfigController extends Controller
223 } 228 }
224 229
225 /** 230 /**
231 * Remove all annotations OR tags OR entries for the current user.
232 *
233 * @Route("/reset/{type}", requirements={"id" = "annotations|tags|entries"}, name="config_reset")
234 *
235 * @return RedirectResponse
236 */
237 public function resetAction($type)
238 {
239 switch ($type) {
240 case 'annotations':
241 $this->getDoctrine()
242 ->getRepository('WallabagAnnotationBundle:Annotation')
243 ->removeAllByUserId($this->getUser()->getId());
244 break;
245
246 case 'tags':
247 $this->removeAllTagsByUserId($this->getUser()->getId());
248 break;
249
250 case 'entries':
251 // SQLite doesn't care about cascading remove, so we need to manually remove associated stuf
252 // otherwise they won't be removed ...
253 if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) {
254 $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId());
255 }
256
257 // manually remove tags to avoid orphan tag
258 $this->removeAllTagsByUserId($this->getUser()->getId());
259
260 $this->getDoctrine()
261 ->getRepository('WallabagCoreBundle:Entry')
262 ->removeAllByUserId($this->getUser()->getId());
263 }
264
265 $this->get('session')->getFlashBag()->add(
266 'notice',
267 'flashes.config.notice.'.$type.'_reset'
268 );
269
270 return $this->redirect($this->generateUrl('config').'#set3');
271 }
272
273 /**
274 * Remove all tags for a given user and cleanup orphan tags.
275 *
276 * @param int $userId
277 */
278 private function removeAllTagsByUserId($userId)
279 {
280 $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId);
281
282 if (empty($tags)) {
283 return;
284 }
285
286 $this->getDoctrine()
287 ->getRepository('WallabagCoreBundle:Entry')
288 ->removeTags($userId, $tags);
289
290 // cleanup orphan tags
291 $em = $this->getDoctrine()->getManager();
292
293 foreach ($tags as $tag) {
294 if (count($tag->getEntries()) === 0) {
295 $em->remove($tag);
296 }
297 }
298
299 $em->flush();
300 }
301
302 /**
226 * Validate that a rule can be edited/deleted by the current user. 303 * Validate that a rule can be edited/deleted by the current user.
227 * 304 *
228 * @param TaggingRule $rule 305 * @param TaggingRule $rule
@@ -253,4 +330,58 @@ class ConfigController extends Controller
253 330
254 return $config; 331 return $config;
255 } 332 }
333
334 /**
335 * Delete account for current user.
336 *
337 * @Route("/account/delete", name="delete_account")
338 *
339 * @param Request $request
340 *
341 * @throws AccessDeniedHttpException
342 *
343 * @return \Symfony\Component\HttpFoundation\RedirectResponse
344 */
345 public function deleteAccountAction(Request $request)
346 {
347 $enabledUsers = $this->getDoctrine()
348 ->getRepository('WallabagUserBundle:User')
349 ->getSumEnabledUsers();
350
351 if ($enabledUsers <= 1) {
352 throw new AccessDeniedHttpException();
353 }
354
355 $user = $this->getUser();
356
357 // logout current user
358 $this->get('security.token_storage')->setToken(null);
359 $request->getSession()->invalidate();
360
361 $em = $this->get('fos_user.user_manager');
362 $em->deleteUser($user);
363
364 return $this->redirect($this->generateUrl('fos_user_security_login'));
365 }
366
367 /**
368 * Switch view mode for current user.
369 *
370 * @Route("/config/view-mode", name="switch_view_mode")
371 *
372 * @param Request $request
373 *
374 * @return \Symfony\Component\HttpFoundation\RedirectResponse
375 */
376 public function changeViewModeAction(Request $request)
377 {
378 $user = $this->getUser();
379 $user->getConfig()->setListMode(!$user->getConfig()->getListMode());
380
381 $em = $this->getDoctrine()->getManager();
382 $em->persist($user);
383 $em->flush();
384
385 return $this->redirect($request->headers->get('referer'));
386 }
256} 387}
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 97bb3d12..f7398e69 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -13,10 +13,45 @@ use Wallabag\CoreBundle\Form\Type\EntryFilterType;
13use Wallabag\CoreBundle\Form\Type\EditEntryType; 13use Wallabag\CoreBundle\Form\Type\EditEntryType;
14use Wallabag\CoreBundle\Form\Type\NewEntryType; 14use Wallabag\CoreBundle\Form\Type\NewEntryType;
15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; 15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
16use Wallabag\CoreBundle\Event\EntrySavedEvent;
17use Wallabag\CoreBundle\Event\EntryDeletedEvent;
18use Wallabag\CoreBundle\Form\Type\SearchEntryType;
16 19
17class EntryController extends Controller 20class EntryController extends Controller
18{ 21{
19 /** 22 /**
23 * @param Request $request
24 * @param int $page
25 *
26 * @Route("/search/{page}", name="search", defaults={"page" = 1})
27 *
28 * Default parameter for page is hardcoded (in duplication of the defaults from the Route)
29 * because this controller is also called inside the layout template without any page as argument
30 *
31 * @return \Symfony\Component\HttpFoundation\Response
32 */
33 public function searchFormAction(Request $request, $page = 1, $currentRoute = null)
34 {
35 // fallback to retrieve currentRoute from query parameter instead of injected one (when using inside a template)
36 if (null === $currentRoute && $request->query->has('currentRoute')) {
37 $currentRoute = $request->query->get('currentRoute');
38 }
39
40 $form = $this->createForm(SearchEntryType::class);
41
42 $form->handleRequest($request);
43
44 if ($form->isSubmitted() && $form->isValid()) {
45 return $this->showEntries('search', $request, $page);
46 }
47
48 return $this->render('WallabagCoreBundle:Entry:search_form.html.twig', [
49 'form' => $form->createView(),
50 'currentRoute' => $currentRoute,
51 ]);
52 }
53
54 /**
20 * Fetch content and update entry. 55 * Fetch content and update entry.
21 * In case it fails, entry will return to avod loosing the data. 56 * In case it fails, entry will return to avod loosing the data.
22 * 57 *
@@ -63,7 +98,7 @@ class EntryController extends Controller
63 98
64 $form->handleRequest($request); 99 $form->handleRequest($request);
65 100
66 if ($form->isValid()) { 101 if ($form->isSubmitted() && $form->isValid()) {
67 $existingEntry = $this->checkIfEntryAlreadyExists($entry); 102 $existingEntry = $this->checkIfEntryAlreadyExists($entry);
68 103
69 if (false !== $existingEntry) { 104 if (false !== $existingEntry) {
@@ -81,6 +116,9 @@ class EntryController extends Controller
81 $em->persist($entry); 116 $em->persist($entry);
82 $em->flush(); 117 $em->flush();
83 118
119 // entry saved, dispatch event about it!
120 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
121
84 return $this->redirect($this->generateUrl('homepage')); 122 return $this->redirect($this->generateUrl('homepage'));
85 } 123 }
86 124
@@ -107,6 +145,9 @@ class EntryController extends Controller
107 $em = $this->getDoctrine()->getManager(); 145 $em = $this->getDoctrine()->getManager();
108 $em->persist($entry); 146 $em->persist($entry);
109 $em->flush(); 147 $em->flush();
148
149 // entry saved, dispatch event about it!
150 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
110 } 151 }
111 152
112 return $this->redirect($this->generateUrl('homepage')); 153 return $this->redirect($this->generateUrl('homepage'));
@@ -140,7 +181,7 @@ class EntryController extends Controller
140 181
141 $form->handleRequest($request); 182 $form->handleRequest($request);
142 183
143 if ($form->isValid()) { 184 if ($form->isSubmitted() && $form->isValid()) {
144 $em = $this->getDoctrine()->getManager(); 185 $em = $this->getDoctrine()->getManager();
145 $em->persist($entry); 186 $em->persist($entry);
146 $em->flush(); 187 $em->flush();
@@ -236,8 +277,14 @@ class EntryController extends Controller
236 private function showEntries($type, Request $request, $page) 277 private function showEntries($type, Request $request, $page)
237 { 278 {
238 $repository = $this->get('wallabag_core.entry_repository'); 279 $repository = $this->get('wallabag_core.entry_repository');
280 $searchTerm = (isset($request->get('search_entry')['term']) ? $request->get('search_entry')['term'] : '');
281 $currentRoute = (!is_null($request->query->get('currentRoute')) ? $request->query->get('currentRoute') : '');
239 282
240 switch ($type) { 283 switch ($type) {
284 case 'search':
285 $qb = $repository->getBuilderForSearchByUser($this->getUser()->getId(), $searchTerm, $currentRoute);
286
287 break;
241 case 'untagged': 288 case 'untagged':
242 $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId()); 289 $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId());
243 290
@@ -272,7 +319,7 @@ class EntryController extends Controller
272 $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $qb); 319 $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $qb);
273 } 320 }
274 321
275 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery()); 322 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false);
276 323
277 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') 324 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')
278 ->prepare($pagerAdapter, $page); 325 ->prepare($pagerAdapter, $page);
@@ -286,11 +333,11 @@ class EntryController extends Controller
286 } 333 }
287 334
288 return $this->render( 335 return $this->render(
289 'WallabagCoreBundle:Entry:entries.html.twig', 336 'WallabagCoreBundle:Entry:entries.html.twig', [
290 [
291 'form' => $form->createView(), 337 'form' => $form->createView(),
292 'entries' => $entries, 338 'entries' => $entries,
293 'currentPage' => $page, 339 'currentPage' => $page,
340 'searchTerm' => $searchTerm,
294 ] 341 ]
295 ); 342 );
296 } 343 }
@@ -343,6 +390,9 @@ class EntryController extends Controller
343 $em->persist($entry); 390 $em->persist($entry);
344 $em->flush(); 391 $em->flush();
345 392
393 // entry saved, dispatch event about it!
394 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
395
346 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()])); 396 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
347 } 397 }
348 398
@@ -431,6 +481,9 @@ class EntryController extends Controller
431 UrlGeneratorInterface::ABSOLUTE_PATH 481 UrlGeneratorInterface::ABSOLUTE_PATH
432 ); 482 );
433 483
484 // entry deleted, dispatch event about it!
485 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
486
434 $em = $this->getDoctrine()->getManager(); 487 $em = $this->getDoctrine()->getManager();
435 $em->remove($entry); 488 $em->remove($entry);
436 $em->flush(); 489 $em->flush();
@@ -486,8 +539,8 @@ class EntryController extends Controller
486 { 539 {
487 $this->checkUserAction($entry); 540 $this->checkUserAction($entry);
488 541
489 if (null === $entry->getUuid()) { 542 if (null === $entry->getUid()) {
490 $entry->generateUuid(); 543 $entry->generateUid();
491 544
492 $em = $this->getDoctrine()->getManager(); 545 $em = $this->getDoctrine()->getManager();
493 $em->persist($entry); 546 $em->persist($entry);
@@ -495,7 +548,7 @@ class EntryController extends Controller
495 } 548 }
496 549
497 return $this->redirect($this->generateUrl('share_entry', [ 550 return $this->redirect($this->generateUrl('share_entry', [
498 'uuid' => $entry->getUuid(), 551 'uid' => $entry->getUid(),
499 ])); 552 ]));
500 } 553 }
501 554
@@ -512,7 +565,7 @@ class EntryController extends Controller
512 { 565 {
513 $this->checkUserAction($entry); 566 $this->checkUserAction($entry);
514 567
515 $entry->cleanUuid(); 568 $entry->cleanUid();
516 569
517 $em = $this->getDoctrine()->getManager(); 570 $em = $this->getDoctrine()->getManager();
518 $em->persist($entry); 571 $em->persist($entry);
@@ -528,7 +581,7 @@ class EntryController extends Controller
528 * 581 *
529 * @param Entry $entry 582 * @param Entry $entry
530 * 583 *
531 * @Route("/share/{uuid}", requirements={"uuid" = ".+"}, name="share_entry") 584 * @Route("/share/{uid}", requirements={"uid" = ".+"}, name="share_entry")
532 * @Cache(maxage="25200", smaxage="25200", public=true) 585 * @Cache(maxage="25200", smaxage="25200", public=true)
533 * 586 *
534 * @return \Symfony\Component\HttpFoundation\Response 587 * @return \Symfony\Component\HttpFoundation\Response
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php
index 79653cfe..abc3336a 100644
--- a/src/Wallabag/CoreBundle/Controller/ExportController.php
+++ b/src/Wallabag/CoreBundle/Controller/ExportController.php
@@ -48,7 +48,7 @@ class ExportController extends Controller
48 * 48 *
49 * @Route("/export/{category}.{format}", name="export_entries", requirements={ 49 * @Route("/export/{category}.{format}", name="export_entries", requirements={
50 * "format": "epub|mobi|pdf|json|xml|txt|csv", 50 * "format": "epub|mobi|pdf|json|xml|txt|csv",
51 * "category": "all|unread|starred|archive|tag_entries|untagged" 51 * "category": "all|unread|starred|archive|tag_entries|untagged|search"
52 * }) 52 * })
53 * 53 *
54 * @return \Symfony\Component\HttpFoundation\Response 54 * @return \Symfony\Component\HttpFoundation\Response
diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php
index 38e3b5a0..92f18707 100644
--- a/src/Wallabag/CoreBundle/Controller/RssController.php
+++ b/src/Wallabag/CoreBundle/Controller/RssController.php
@@ -3,12 +3,15 @@
3namespace Wallabag\CoreBundle\Controller; 3namespace Wallabag\CoreBundle\Controller;
4 4
5use Pagerfanta\Adapter\DoctrineORMAdapter; 5use Pagerfanta\Adapter\DoctrineORMAdapter;
6use Pagerfanta\Exception\OutOfRangeCurrentPageException;
6use Pagerfanta\Pagerfanta; 7use Pagerfanta\Pagerfanta;
7use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; 8use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
8use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 9use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
10use Symfony\Component\HttpFoundation\Request;
9use Symfony\Bundle\FrameworkBundle\Controller\Controller; 11use Symfony\Bundle\FrameworkBundle\Controller\Controller;
10use Wallabag\CoreBundle\Entity\Entry; 12use Wallabag\CoreBundle\Entity\Entry;
11use Wallabag\UserBundle\Entity\User; 13use Wallabag\UserBundle\Entity\User;
14use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
12 15
13class RssController extends Controller 16class RssController extends Controller
14{ 17{
@@ -20,9 +23,9 @@ class RssController extends Controller
20 * 23 *
21 * @return \Symfony\Component\HttpFoundation\Response 24 * @return \Symfony\Component\HttpFoundation\Response
22 */ 25 */
23 public function showUnreadAction(User $user) 26 public function showUnreadAction(Request $request, User $user)
24 { 27 {
25 return $this->showEntries('unread', $user); 28 return $this->showEntries('unread', $user, $request->query->get('page', 1));
26 } 29 }
27 30
28 /** 31 /**
@@ -33,9 +36,9 @@ class RssController extends Controller
33 * 36 *
34 * @return \Symfony\Component\HttpFoundation\Response 37 * @return \Symfony\Component\HttpFoundation\Response
35 */ 38 */
36 public function showArchiveAction(User $user) 39 public function showArchiveAction(Request $request, User $user)
37 { 40 {
38 return $this->showEntries('archive', $user); 41 return $this->showEntries('archive', $user, $request->query->get('page', 1));
39 } 42 }
40 43
41 /** 44 /**
@@ -46,9 +49,9 @@ class RssController extends Controller
46 * 49 *
47 * @return \Symfony\Component\HttpFoundation\Response 50 * @return \Symfony\Component\HttpFoundation\Response
48 */ 51 */
49 public function showStarredAction(User $user) 52 public function showStarredAction(Request $request, User $user)
50 { 53 {
51 return $this->showEntries('starred', $user); 54 return $this->showEntries('starred', $user, $request->query->get('page', 1));
52 } 55 }
53 56
54 /** 57 /**
@@ -57,10 +60,11 @@ class RssController extends Controller
57 * 60 *
58 * @param string $type Entries type: unread, starred or archive 61 * @param string $type Entries type: unread, starred or archive
59 * @param User $user 62 * @param User $user
63 * @param int $page
60 * 64 *
61 * @return \Symfony\Component\HttpFoundation\Response 65 * @return \Symfony\Component\HttpFoundation\Response
62 */ 66 */
63 private function showEntries($type, User $user) 67 private function showEntries($type, User $user, $page = 1)
64 { 68 {
65 $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); 69 $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry');
66 70
@@ -81,14 +85,32 @@ class RssController extends Controller
81 throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); 85 throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
82 } 86 }
83 87
84 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery()); 88 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false);
85 $entries = new Pagerfanta($pagerAdapter); 89 $entries = new Pagerfanta($pagerAdapter);
86 90
87 $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit'); 91 $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit');
88 $entries->setMaxPerPage($perPage); 92 $entries->setMaxPerPage($perPage);
89 93
94 $url = $this->generateUrl(
95 $type.'_rss',
96 [
97 'username' => $user->getUsername(),
98 'token' => $user->getConfig()->getRssToken(),
99 ],
100 UrlGeneratorInterface::ABSOLUTE_URL
101 );
102
103 try {
104 $entries->setCurrentPage((int) $page);
105 } catch (OutOfRangeCurrentPageException $e) {
106 if ($page > 1) {
107 return $this->redirect($url.'?page='.$entries->getNbPages(), 302);
108 }
109 }
110
90 return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ 111 return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [
91 'type' => $type, 112 'type' => $type,
113 'url' => $url,
92 'entries' => $entries, 114 'entries' => $entries,
93 ]); 115 ]);
94 } 116 }
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php
index 707f3bbe..8a093289 100644
--- a/src/Wallabag/CoreBundle/Controller/TagController.php
+++ b/src/Wallabag/CoreBundle/Controller/TagController.php
@@ -27,7 +27,7 @@ class TagController extends Controller
27 $form = $this->createForm(NewTagType::class, new Tag()); 27 $form = $this->createForm(NewTagType::class, new Tag());
28 $form->handleRequest($request); 28 $form->handleRequest($request);
29 29
30 if ($form->isValid()) { 30 if ($form->isSubmitted() && $form->isValid()) {
31 $this->get('wallabag_core.content_proxy')->assignTagsToEntry( 31 $this->get('wallabag_core.content_proxy')->assignTagsToEntry(
32 $entry, 32 $entry,
33 $form->get('label')->getData() 33 $form->get('label')->getData()
@@ -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 }
@@ -143,7 +143,7 @@ class TagController extends Controller
143 'form' => null, 143 'form' => null,
144 'entries' => $entries, 144 'entries' => $entries,
145 'currentPage' => $page, 145 'currentPage' => $page,
146 'tag' => $tag->getLabel(), 146 'tag' => $tag->getSlug(),
147 ]); 147 ]);
148 } 148 }
149} 149}
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
index 921c739f..3d4d5def 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
@@ -21,6 +21,8 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
21 $adminConfig->setReadingSpeed(1); 21 $adminConfig->setReadingSpeed(1);
22 $adminConfig->setLanguage('en'); 22 $adminConfig->setLanguage('en');
23 $adminConfig->setPocketConsumerKey('xxxxx'); 23 $adminConfig->setPocketConsumerKey('xxxxx');
24 $adminConfig->setActionMarkAsRead(0);
25 $adminConfig->setListMode(0);
24 26
25 $manager->persist($adminConfig); 27 $manager->persist($adminConfig);
26 28
@@ -32,6 +34,8 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
32 $bobConfig->setReadingSpeed(1); 34 $bobConfig->setReadingSpeed(1);
33 $bobConfig->setLanguage('fr'); 35 $bobConfig->setLanguage('fr');
34 $bobConfig->setPocketConsumerKey(null); 36 $bobConfig->setPocketConsumerKey(null);
37 $bobConfig->setActionMarkAsRead(1);
38 $bobConfig->setListMode(1);
35 39
36 $manager->persist($bobConfig); 40 $manager->persist($bobConfig);
37 41
@@ -43,6 +47,8 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
43 $emptyConfig->setReadingSpeed(1); 47 $emptyConfig->setReadingSpeed(1);
44 $emptyConfig->setLanguage('en'); 48 $emptyConfig->setLanguage('en');
45 $emptyConfig->setPocketConsumerKey(null); 49 $emptyConfig->setPocketConsumerKey(null);
50 $emptyConfig->setActionMarkAsRead(0);
51 $emptyConfig->setListMode(0);
46 52
47 $manager->persist($emptyConfig); 53 $manager->persist($emptyConfig);
48 54
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
index a5e1be65..a723656e 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
@@ -36,6 +36,16 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
36 'section' => 'entry', 36 'section' => 'entry',
37 ], 37 ],
38 [ 38 [
39 'name' => 'share_unmark',
40 'value' => '1',
41 'section' => 'entry',
42 ],
43 [
44 'name' => 'unmark_url',
45 'value' => 'https://unmark.it',
46 'section' => 'entry',
47 ],
48 [
39 'name' => 'share_shaarli', 49 'name' => 'share_shaarli',
40 'value' => '1', 50 'value' => '1',
41 'section' => 'entry', 51 'section' => 'entry',
@@ -140,6 +150,16 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
140 'value' => 'wallabag', 150 'value' => 'wallabag',
141 'section' => 'misc', 151 'section' => 'misc',
142 ], 152 ],
153 [
154 'name' => 'download_images_enabled',
155 'value' => '0',
156 'section' => 'misc',
157 ],
158 [
159 'name' => 'restricted_access',
160 'value' => '0',
161 'section' => 'entry',
162 ],
143 ]; 163 ];
144 164
145 foreach ($settings as $setting) { 165 foreach ($settings as $setting) {
@@ -158,6 +178,6 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
158 */ 178 */
159 public function getOrder() 179 public function getOrder()
160 { 180 {
161 return 50; 181 return 29;
162 } 182 }
163} 183}
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php
index 09e99f36..6de561e0 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php
@@ -15,7 +15,7 @@ class LoadTagData extends AbstractFixture implements OrderedFixtureInterface
15 public function load(ObjectManager $manager) 15 public function load(ObjectManager $manager)
16 { 16 {
17 $tag1 = new Tag(); 17 $tag1 = new Tag();
18 $tag1->setLabel('foo'); 18 $tag1->setLabel('foo bar');
19 19
20 $manager->persist($tag1); 20 $manager->persist($tag1);
21 21
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
index 3a3da024..006a18c3 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
+++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
@@ -41,6 +41,12 @@ class Configuration implements ConfigurationInterface
41 ->end() 41 ->end()
42 ->scalarNode('fetching_error_message') 42 ->scalarNode('fetching_error_message')
43 ->end() 43 ->end()
44 ->scalarNode('action_mark_as_read')
45 ->defaultValue(1)
46 ->end()
47 ->scalarNode('list_mode')
48 ->defaultValue(1)
49 ->end()
44 ->end() 50 ->end()
45 ; 51 ;
46 52
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
index b4992d54..aa9ee339 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
+++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
@@ -23,6 +23,8 @@ class WallabagCoreExtension extends Extension
23 $container->setParameter('wallabag_core.version', $config['version']); 23 $container->setParameter('wallabag_core.version', $config['version']);
24 $container->setParameter('wallabag_core.paypal_url', $config['paypal_url']); 24 $container->setParameter('wallabag_core.paypal_url', $config['paypal_url']);
25 $container->setParameter('wallabag_core.cache_lifetime', $config['cache_lifetime']); 25 $container->setParameter('wallabag_core.cache_lifetime', $config['cache_lifetime']);
26 $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']);
27 $container->setParameter('wallabag_core.list_mode', $config['list_mode']);
26 $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); 28 $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']);
27 29
28 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 30 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php
index 69393ac9..b902ae2c 100644
--- a/src/Wallabag/CoreBundle/Entity/Config.php
+++ b/src/Wallabag/CoreBundle/Entity/Config.php
@@ -16,6 +16,9 @@ use Wallabag\UserBundle\Entity\User;
16 */ 16 */
17class Config 17class Config
18{ 18{
19 const REDIRECT_TO_HOMEPAGE = 0;
20 const REDIRECT_TO_CURRENT_PAGE = 1;
21
19 /** 22 /**
20 * @var int 23 * @var int
21 * 24 *
@@ -88,6 +91,20 @@ class Config
88 private $pocketConsumerKey; 91 private $pocketConsumerKey;
89 92
90 /** 93 /**
94 * @var int
95 *
96 * @ORM\Column(name="action_mark_as_read", type="integer", nullable=true, options={"default" = 0})
97 */
98 private $actionMarkAsRead;
99
100 /**
101 * @var int
102 *
103 * @ORM\Column(name="list_mode", type="integer", nullable=true)
104 */
105 private $listMode;
106
107 /**
91 * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config") 108 * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config")
92 */ 109 */
93 private $user; 110 private $user;
@@ -310,6 +327,46 @@ class Config
310 } 327 }
311 328
312 /** 329 /**
330 * @return int
331 */
332 public function getActionMarkAsRead()
333 {
334 return $this->actionMarkAsRead;
335 }
336
337 /**
338 * @param int $actionMarkAsRead
339 *
340 * @return Config
341 */
342 public function setActionMarkAsRead($actionMarkAsRead)
343 {
344 $this->actionMarkAsRead = $actionMarkAsRead;
345
346 return $this;
347 }
348
349 /**
350 * @return int
351 */
352 public function getListMode()
353 {
354 return $this->listMode;
355 }
356
357 /**
358 * @param int $listMode
359 *
360 * @return Config
361 */
362 public function setListMode($listMode)
363 {
364 $this->listMode = $listMode;
365
366 return $this;
367 }
368
369 /**
313 * @param TaggingRule $rule 370 * @param TaggingRule $rule
314 * 371 *
315 * @return Config 372 * @return Config
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index f2da3f4d..7276b437 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -19,7 +19,14 @@ use Wallabag\AnnotationBundle\Entity\Annotation;
19 * 19 *
20 * @XmlRoot("entry") 20 * @XmlRoot("entry")
21 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository") 21 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository")
22 * @ORM\Table(name="`entry`") 22 * @ORM\Table(
23 * name="`entry`",
24 * options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"},
25 * indexes={
26 * @ORM\Index(name="created_at", columns={"created_at"}),
27 * @ORM\Index(name="uid", columns={"uid"})
28 * }
29 * )
23 * @ORM\HasLifecycleCallbacks() 30 * @ORM\HasLifecycleCallbacks()
24 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())") 31 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())")
25 */ 32 */
@@ -40,11 +47,11 @@ class Entry
40 /** 47 /**
41 * @var string 48 * @var string
42 * 49 *
43 * @ORM\Column(name="uuid", type="text", nullable=true) 50 * @ORM\Column(name="uid", type="string", length=23, nullable=true)
44 * 51 *
45 * @Groups({"entries_for_user", "export_all"}) 52 * @Groups({"entries_for_user", "export_all"})
46 */ 53 */
47 private $uuid; 54 private $uid;
48 55
49 /** 56 /**
50 * @var string 57 * @var string
@@ -177,6 +184,15 @@ class Entry
177 private $isPublic; 184 private $isPublic;
178 185
179 /** 186 /**
187 * @var string
188 *
189 * @ORM\Column(name="http_status", type="string", length=3, nullable=true)
190 *
191 * @Groups({"entries_for_user", "export_all"})
192 */
193 private $httpStatus;
194
195 /**
180 * @Exclude 196 * @Exclude
181 * 197 *
182 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="entries") 198 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="entries")
@@ -190,10 +206,10 @@ class Entry
190 * @ORM\JoinTable( 206 * @ORM\JoinTable(
191 * name="entry_tag", 207 * name="entry_tag",
192 * joinColumns={ 208 * joinColumns={
193 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") 209 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade")
194 * }, 210 * },
195 * inverseJoinColumns={ 211 * inverseJoinColumns={
196 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id") 212 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="cascade")
197 * } 213 * }
198 * ) 214 * )
199 */ 215 */
@@ -636,33 +652,53 @@ class Entry
636 /** 652 /**
637 * @return string 653 * @return string
638 */ 654 */
639 public function getUuid() 655 public function getUid()
640 { 656 {
641 return $this->uuid; 657 return $this->uid;
642 } 658 }
643 659
644 /** 660 /**
645 * @param string $uuid 661 * @param string $uid
646 * 662 *
647 * @return Entry 663 * @return Entry
648 */ 664 */
649 public function setUuid($uuid) 665 public function setUid($uid)
650 { 666 {
651 $this->uuid = $uuid; 667 $this->uid = $uid;
652 668
653 return $this; 669 return $this;
654 } 670 }
655 671
656 public function generateUuid() 672 public function generateUid()
657 { 673 {
658 if (null === $this->uuid) { 674 if (null === $this->uid) {
659 // @see http://blog.kevingomez.fr/til/2015/07/26/why-is-uniqid-slow/ for true parameter 675 // @see http://blog.kevingomez.fr/til/2015/07/26/why-is-uniqid-slow/ for true parameter
660 $this->uuid = uniqid('', true); 676 $this->uid = uniqid('', true);
661 } 677 }
662 } 678 }
663 679
664 public function cleanUuid() 680 public function cleanUid()
681 {
682 $this->uid = null;
683 }
684
685 /**
686 * @return int
687 */
688 public function getHttpStatus()
689 {
690 return $this->httpStatus;
691 }
692
693 /**
694 * @param int $httpStatus
695 *
696 * @return Entry
697 */
698 public function setHttpStatus($httpStatus)
665 { 699 {
666 $this->uuid = null; 700 $this->httpStatus = $httpStatus;
701
702 return $this;
667 } 703 }
668} 704}
diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php
index 28914cc1..72651b19 100644
--- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php
+++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php
@@ -28,6 +28,7 @@ class TaggingRule
28 * @var string 28 * @var string
29 * 29 *
30 * @Assert\NotBlank() 30 * @Assert\NotBlank()
31 * @Assert\Length(max=255)
31 * @RulerZAssert\ValidRule( 32 * @RulerZAssert\ValidRule(
32 * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, 33 * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"},
33 * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"} 34 * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"}
diff --git a/src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php b/src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php
new file mode 100644
index 00000000..e9061d04
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php
@@ -0,0 +1,26 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event;
4
5use Symfony\Component\EventDispatcher\Event;
6use Wallabag\CoreBundle\Entity\Entry;
7
8/**
9 * This event is fired as soon as an entry is deleted.
10 */
11class EntryDeletedEvent extends Event
12{
13 const NAME = 'entry.deleted';
14
15 protected $entry;
16
17 public function __construct(Entry $entry)
18 {
19 $this->entry = $entry;
20 }
21
22 public function getEntry()
23 {
24 return $this->entry;
25 }
26}
diff --git a/src/Wallabag/CoreBundle/Event/EntrySavedEvent.php b/src/Wallabag/CoreBundle/Event/EntrySavedEvent.php
new file mode 100644
index 00000000..5fdb5221
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/EntrySavedEvent.php
@@ -0,0 +1,26 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event;
4
5use Symfony\Component\EventDispatcher\Event;
6use Wallabag\CoreBundle\Entity\Entry;
7
8/**
9 * This event is fired as soon as an entry was saved.
10 */
11class EntrySavedEvent extends Event
12{
13 const NAME = 'entry.saved';
14
15 protected $entry;
16
17 public function __construct(Entry $entry)
18 {
19 $this->entry = $entry;
20 }
21
22 public function getEntry()
23 {
24 return $this->entry;
25 }
26}
diff --git a/src/Wallabag/CoreBundle/EventListener/LocaleListener.php b/src/Wallabag/CoreBundle/Event/Listener/LocaleListener.php
index a1c7e5ab..b435d99e 100644
--- a/src/Wallabag/CoreBundle/EventListener/LocaleListener.php
+++ b/src/Wallabag/CoreBundle/Event/Listener/LocaleListener.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\EventListener; 3namespace Wallabag\CoreBundle\Event\Listener;
4 4
5use Symfony\Component\EventDispatcher\EventSubscriberInterface; 5use Symfony\Component\EventDispatcher\EventSubscriberInterface;
6use Symfony\Component\HttpKernel\Event\GetResponseEvent; 6use Symfony\Component\HttpKernel\Event\GetResponseEvent;
diff --git a/src/Wallabag/CoreBundle/EventListener/UserLocaleListener.php b/src/Wallabag/CoreBundle/Event/Listener/UserLocaleListener.php
index 82d1a63a..367cdfb0 100644
--- a/src/Wallabag/CoreBundle/EventListener/UserLocaleListener.php
+++ b/src/Wallabag/CoreBundle/Event/Listener/UserLocaleListener.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\EventListener; 3namespace Wallabag\CoreBundle\Event\Listener;
4 4
5use Symfony\Component\HttpFoundation\Session\Session; 5use Symfony\Component\HttpFoundation\Session\Session;
6use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; 6use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php
new file mode 100644
index 00000000..4ebe837b
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php
@@ -0,0 +1,121 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event\Subscriber;
4
5use Symfony\Component\EventDispatcher\EventSubscriberInterface;
6use Psr\Log\LoggerInterface;
7use Wallabag\CoreBundle\Helper\DownloadImages;
8use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Event\EntrySavedEvent;
10use Wallabag\CoreBundle\Event\EntryDeletedEvent;
11use Doctrine\ORM\EntityManager;
12
13class DownloadImagesSubscriber implements EventSubscriberInterface
14{
15 private $em;
16 private $downloadImages;
17 private $enabled;
18 private $logger;
19
20 public function __construct(EntityManager $em, DownloadImages $downloadImages, $enabled, LoggerInterface $logger)
21 {
22 $this->em = $em;
23 $this->downloadImages = $downloadImages;
24 $this->enabled = $enabled;
25 $this->logger = $logger;
26 }
27
28 public static function getSubscribedEvents()
29 {
30 return [
31 EntrySavedEvent::NAME => 'onEntrySaved',
32 EntryDeletedEvent::NAME => 'onEntryDeleted',
33 ];
34 }
35
36 /**
37 * Download images and updated the data into the entry.
38 *
39 * @param EntrySavedEvent $event
40 */
41 public function onEntrySaved(EntrySavedEvent $event)
42 {
43 if (!$this->enabled) {
44 $this->logger->debug('DownloadImagesSubscriber: disabled.');
45
46 return;
47 }
48
49 $entry = $event->getEntry();
50
51 $html = $this->downloadImages($entry);
52 if (false !== $html) {
53 $this->logger->debug('DownloadImagesSubscriber: updated html.');
54
55 $entry->setContent($html);
56 }
57
58 // update preview picture
59 $previewPicture = $this->downloadPreviewImage($entry);
60 if (false !== $previewPicture) {
61 $this->logger->debug('DownloadImagesSubscriber: update preview picture.');
62
63 $entry->setPreviewPicture($previewPicture);
64 }
65
66 $this->em->persist($entry);
67 $this->em->flush();
68 }
69
70 /**
71 * Remove images related to the entry.
72 *
73 * @param EntryDeletedEvent $event
74 */
75 public function onEntryDeleted(EntryDeletedEvent $event)
76 {
77 if (!$this->enabled) {
78 $this->logger->debug('DownloadImagesSubscriber: disabled.');
79
80 return;
81 }
82
83 $this->downloadImages->removeImages($event->getEntry()->getId());
84 }
85
86 /**
87 * Download all images from the html.
88 *
89 * @todo If we want to add async download, it should be done in that method
90 *
91 * @param Entry $entry
92 *
93 * @return string|false False in case of async
94 */
95 private function downloadImages(Entry $entry)
96 {
97 return $this->downloadImages->processHtml(
98 $entry->getId(),
99 $entry->getContent(),
100 $entry->getUrl()
101 );
102 }
103
104 /**
105 * Download the preview picture.
106 *
107 * @todo If we want to add async download, it should be done in that method
108 *
109 * @param Entry $entry
110 *
111 * @return string|false False in case of async
112 */
113 private function downloadPreviewImage(Entry $entry)
114 {
115 return $this->downloadImages->processSingleImage(
116 $entry->getId(),
117 $entry->getPreviewPicture(),
118 $entry->getUrl()
119 );
120 }
121}
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
new file mode 100644
index 00000000..3b4c4cf9
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
@@ -0,0 +1,70 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event\Subscriber;
4
5use Doctrine\Common\EventSubscriber;
6use Doctrine\ORM\Event\LifecycleEventArgs;
7use Wallabag\CoreBundle\Entity\Entry;
8use Doctrine\Bundle\DoctrineBundle\Registry;
9
10/**
11 * SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry.
12 * Foreign Key Support can be enabled by running `PRAGMA foreign_keys = ON;` at runtime (AT RUNTIME !).
13 * But it needs a compilation flag that not all SQLite instance has ...
14 *
15 * @see https://www.sqlite.org/foreignkeys.html#fk_enable
16 */
17class SQLiteCascadeDeleteSubscriber implements EventSubscriber
18{
19 private $doctrine;
20
21 /**
22 * @param \Doctrine\Bundle\DoctrineBundle\Registry $doctrine
23 */
24 public function __construct(Registry $doctrine)
25 {
26 $this->doctrine = $doctrine;
27 }
28
29 /**
30 * @return array
31 */
32 public function getSubscribedEvents()
33 {
34 return [
35 'preRemove',
36 ];
37 }
38
39 /**
40 * We removed everything related to the upcoming removed entry because SQLite can't handle it on it own.
41 * We do it in the preRemove, because we can't retrieve tags in the postRemove (because the entry id is gone).
42 *
43 * @param LifecycleEventArgs $args
44 */
45 public function preRemove(LifecycleEventArgs $args)
46 {
47 $entity = $args->getEntity();
48
49 if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver ||
50 !$entity instanceof Entry) {
51 return;
52 }
53
54 $em = $this->doctrine->getManager();
55
56 if (null !== $entity->getTags()) {
57 foreach ($entity->getTags() as $tag) {
58 $entity->removeTag($tag);
59 }
60 }
61
62 if (null !== $entity->getAnnotations()) {
63 foreach ($entity->getAnnotations() as $annotation) {
64 $em->remove($annotation);
65 }
66 }
67
68 $em->flush();
69 }
70}
diff --git a/src/Wallabag/CoreBundle/Subscriber/TablePrefixSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php
index 0379ad6a..711c3bf8 100644
--- a/src/Wallabag/CoreBundle/Subscriber/TablePrefixSubscriber.php
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\Subscriber; 3namespace Wallabag\CoreBundle\Event\Subscriber;
4 4
5use Doctrine\Common\EventSubscriber; 5use Doctrine\Common\EventSubscriber;
6use Doctrine\ORM\Event\LoadClassMetadataEventArgs; 6use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
@@ -39,7 +39,7 @@ class TablePrefixSubscriber implements EventSubscriber
39 return; 39 return;
40 } 40 }
41 41
42 $classMetadata->setTableName($this->prefix.$classMetadata->getTableName()); 42 $classMetadata->setPrimaryTable(['name' => $this->prefix.$classMetadata->getTableName()]);
43 43
44 foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { 44 foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
45 if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) { 45 if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) {
diff --git a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
index 0bac2874..7e3b9dd4 100644
--- a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
@@ -7,6 +7,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
7use Symfony\Component\Form\Extension\Core\Type\SubmitType; 7use Symfony\Component\Form\Extension\Core\Type\SubmitType;
8use Symfony\Component\Form\FormBuilderInterface; 8use Symfony\Component\Form\FormBuilderInterface;
9use Symfony\Component\OptionsResolver\OptionsResolver; 9use Symfony\Component\OptionsResolver\OptionsResolver;
10use Wallabag\CoreBundle\Entity\Config;
10 11
11class ConfigType extends AbstractType 12class ConfigType extends AbstractType
12{ 13{
@@ -48,6 +49,13 @@ class ConfigType extends AbstractType
48 'config.form_settings.reading_speed.400_word' => '2', 49 'config.form_settings.reading_speed.400_word' => '2',
49 ], 50 ],
50 ]) 51 ])
52 ->add('action_mark_as_read', ChoiceType::class, [
53 'label' => 'config.form_settings.action_mark_as_read.label',
54 'choices' => [
55 'config.form_settings.action_mark_as_read.redirect_homepage' => Config::REDIRECT_TO_HOMEPAGE,
56 'config.form_settings.action_mark_as_read.redirect_current_page' => Config::REDIRECT_TO_CURRENT_PAGE,
57 ],
58 ])
51 ->add('language', ChoiceType::class, [ 59 ->add('language', ChoiceType::class, [
52 'choices' => array_flip($this->languages), 60 'choices' => array_flip($this->languages),
53 'label' => 'config.form_settings.language_label', 61 'label' => 'config.form_settings.language_label',
diff --git a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php
index a3e36fdd..ee66c728 100644
--- a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php
@@ -11,6 +11,7 @@ use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\CheckboxFilterType;
11use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType; 11use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType;
12use Symfony\Component\Form\AbstractType; 12use Symfony\Component\Form\AbstractType;
13use Symfony\Component\Form\FormBuilderInterface; 13use Symfony\Component\Form\FormBuilderInterface;
14use Symfony\Component\HttpFoundation\Response;
14use Symfony\Component\OptionsResolver\OptionsResolver; 15use Symfony\Component\OptionsResolver\OptionsResolver;
15use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 16use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
16 17
@@ -89,12 +90,27 @@ class EntryFilterType extends AbstractType
89 if (strlen($value) <= 2 || empty($value)) { 90 if (strlen($value) <= 2 || empty($value)) {
90 return; 91 return;
91 } 92 }
92 $expression = $filterQuery->getExpr()->like($field, $filterQuery->getExpr()->literal('%'.$value.'%')); 93 $expression = $filterQuery->getExpr()->like($field, $filterQuery->getExpr()->lower($filterQuery->getExpr()->literal('%'.$value.'%')));
93 94
94 return $filterQuery->createCondition($expression); 95 return $filterQuery->createCondition($expression);
95 }, 96 },
96 'label' => 'entry.filters.domain_label', 97 'label' => 'entry.filters.domain_label',
97 ]) 98 ])
99 ->add('httpStatus', TextFilterType::class, [
100 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) {
101 $value = $values['value'];
102 if (false === array_key_exists($value, Response::$statusTexts)) {
103 return;
104 }
105
106 $paramName = sprintf('%s', str_replace('.', '_', $field));
107 $expression = $filterQuery->getExpr()->eq($field, ':'.$paramName);
108 $parameters = array($paramName => $value);
109
110 return $filterQuery->createCondition($expression, $parameters);
111 },
112 'label' => 'entry.filters.http_status_label',
113 ])
98 ->add('isArchived', CheckboxFilterType::class, [ 114 ->add('isArchived', CheckboxFilterType::class, [
99 'label' => 'entry.filters.archived_label', 115 'label' => 'entry.filters.archived_label',
100 ]) 116 ])
diff --git a/src/Wallabag/CoreBundle/Form/Type/NewTagType.php b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
index 3db4105f..e830ade4 100644
--- a/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
@@ -3,6 +3,7 @@
3namespace Wallabag\CoreBundle\Form\Type; 3namespace Wallabag\CoreBundle\Form\Type;
4 4
5use Symfony\Component\Form\AbstractType; 5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\SubmitType;
6use Symfony\Component\Form\Extension\Core\Type\TextType; 7use Symfony\Component\Form\Extension\Core\Type\TextType;
7use Symfony\Component\Form\FormBuilderInterface; 8use Symfony\Component\Form\FormBuilderInterface;
8use Symfony\Component\OptionsResolver\OptionsResolver; 9use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -12,7 +13,15 @@ class NewTagType extends AbstractType
12 public function buildForm(FormBuilderInterface $builder, array $options) 13 public function buildForm(FormBuilderInterface $builder, array $options)
13 { 14 {
14 $builder 15 $builder
15 ->add('label', TextType::class, ['required' => true]) 16 ->add('label', TextType::class, [
17 'required' => true,
18 'attr' => [
19 'placeholder' => 'tag.new.placeholder',
20 ],
21 ])
22 ->add('add', SubmitType::class, [
23 'label' => 'tag.new.add',
24 ])
16 ; 25 ;
17 } 26 }
18 27
diff --git a/src/Wallabag/CoreBundle/Form/Type/SearchEntryType.php b/src/Wallabag/CoreBundle/Form/Type/SearchEntryType.php
new file mode 100644
index 00000000..b56cae8e
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/SearchEntryType.php
@@ -0,0 +1,29 @@
1<?php
2
3namespace Wallabag\CoreBundle\Form\Type;
4
5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\TextType;
7use Symfony\Component\Form\FormBuilderInterface;
8use Symfony\Component\OptionsResolver\OptionsResolver;
9
10class SearchEntryType extends AbstractType
11{
12 public function buildForm(FormBuilderInterface $builder, array $options)
13 {
14 $builder
15 ->setMethod('GET')
16 ->add('term', TextType::class, [
17 'required' => true,
18 'label' => 'entry.new.form_search.term_label',
19 ])
20 ;
21 }
22
23 public function configureOptions(OptionsResolver $resolver)
24 {
25 $resolver->setDefaults([
26 'csrf_protection' => false,
27 ]);
28 }
29}
diff --git a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
new file mode 100644
index 00000000..6d4129e8
--- /dev/null
+++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
@@ -0,0 +1,68 @@
1<?php
2
3namespace Wallabag\CoreBundle\GuzzleSiteAuthenticator;
4
5use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig;
6use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfigBuilder;
7use Graby\SiteConfig\ConfigBuilder;
8use OutOfRangeException;
9
10class GrabySiteConfigBuilder implements SiteConfigBuilder
11{
12 /**
13 * @var \Graby\SiteConfig\ConfigBuilder
14 */
15 private $grabyConfigBuilder;
16 /**
17 * @var array
18 */
19 private $credentials;
20
21 /**
22 * GrabySiteConfigBuilder constructor.
23 *
24 * @param \Graby\SiteConfig\ConfigBuilder $grabyConfigBuilder
25 * @param array $credentials
26 */
27 public function __construct(ConfigBuilder $grabyConfigBuilder, array $credentials = [])
28 {
29 $this->grabyConfigBuilder = $grabyConfigBuilder;
30 $this->credentials = $credentials;
31 }
32
33 /**
34 * Builds the SiteConfig for a host.
35 *
36 * @param string $host The "www." prefix is ignored
37 *
38 * @return SiteConfig
39 *
40 * @throws OutOfRangeException If there is no config for $host
41 */
42 public function buildForHost($host)
43 {
44 // required by credentials below
45 $host = strtolower($host);
46 if (substr($host, 0, 4) == 'www.') {
47 $host = substr($host, 4);
48 }
49
50 $config = $this->grabyConfigBuilder->buildForHost($host);
51 $parameters = [
52 'host' => $host,
53 'requiresLogin' => $config->requires_login ?: false,
54 'loginUri' => $config->login_uri ?: null,
55 'usernameField' => $config->login_username_field ?: null,
56 'passwordField' => $config->login_password_field ?: null,
57 'extraFields' => is_array($config->login_extra_fields) ? $config->login_extra_fields : [],
58 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null,
59 ];
60
61 if (isset($this->credentials[$host])) {
62 $parameters['username'] = $this->credentials[$host]['username'];
63 $parameters['password'] = $this->credentials[$host]['password'];
64 }
65
66 return new SiteConfig($parameters);
67 }
68}
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index bbd5db5d..f222dd88 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;
@@ -21,14 +21,16 @@ class ContentProxy
21 protected $logger; 21 protected $logger;
22 protected $tagRepository; 22 protected $tagRepository;
23 protected $mimeGuesser; 23 protected $mimeGuesser;
24 protected $fetchingErrorMessage;
24 25
25 public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, Logger $logger) 26 public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, LoggerInterface $logger, $fetchingErrorMessage)
26 { 27 {
27 $this->graby = $graby; 28 $this->graby = $graby;
28 $this->tagger = $tagger; 29 $this->tagger = $tagger;
29 $this->logger = $logger; 30 $this->logger = $logger;
30 $this->tagRepository = $tagRepository; 31 $this->tagRepository = $tagRepository;
31 $this->mimeGuesser = new MimeTypeExtensionGuesser(); 32 $this->mimeGuesser = new MimeTypeExtensionGuesser();
33 $this->fetchingErrorMessage = $fetchingErrorMessage;
32 } 34 }
33 35
34 /** 36 /**
@@ -48,7 +50,13 @@ class ContentProxy
48 { 50 {
49 // do we have to fetch the content or the provided one is ok? 51 // do we have to fetch the content or the provided one is ok?
50 if (empty($content) || false === $this->validateContent($content)) { 52 if (empty($content) || false === $this->validateContent($content)) {
51 $content = $this->graby->fetchContent($url); 53 $fetchedContent = $this->graby->fetchContent($url);
54
55 // when content is imported, we have information in $content
56 // in case fetching content goes bad, we'll keep the imported information instead of overriding them
57 if (empty($content) || $fetchedContent['html'] !== $this->fetchingErrorMessage) {
58 $content = $fetchedContent;
59 }
52 } 60 }
53 61
54 $title = $content['title']; 62 $title = $content['title'];
@@ -58,7 +66,7 @@ class ContentProxy
58 66
59 $html = $content['html']; 67 $html = $content['html'];
60 if (false === $html) { 68 if (false === $html) {
61 $html = '<p>Unable to retrieve readable content.</p>'; 69 $html = $this->fetchingErrorMessage;
62 70
63 if (isset($content['open_graph']['og_description'])) { 71 if (isset($content['open_graph']['og_description'])) {
64 $html .= '<p><i>But we found a short description: </i></p>'; 72 $html .= '<p><i>But we found a short description: </i></p>';
@@ -69,8 +77,10 @@ class ContentProxy
69 $entry->setUrl($content['url'] ?: $url); 77 $entry->setUrl($content['url'] ?: $url);
70 $entry->setTitle($title); 78 $entry->setTitle($title);
71 $entry->setContent($html); 79 $entry->setContent($html);
72 $entry->setLanguage($content['language']); 80 $entry->setHttpStatus(isset($content['status']) ? $content['status'] : '');
73 $entry->setMimetype($content['content_type']); 81
82 $entry->setLanguage(isset($content['language']) ? $content['language'] : '');
83 $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : '');
74 $entry->setReadingTime(Utils::getReadingTime($html)); 84 $entry->setReadingTime(Utils::getReadingTime($html));
75 85
76 $domainName = parse_url($entry->getUrl(), PHP_URL_HOST); 86 $domainName = parse_url($entry->getUrl(), PHP_URL_HOST);
@@ -78,12 +88,12 @@ class ContentProxy
78 $entry->setDomainName($domainName); 88 $entry->setDomainName($domainName);
79 } 89 }
80 90
81 if (isset($content['open_graph']['og_image'])) { 91 if (isset($content['open_graph']['og_image']) && $content['open_graph']['og_image']) {
82 $entry->setPreviewPicture($content['open_graph']['og_image']); 92 $entry->setPreviewPicture($content['open_graph']['og_image']);
83 } 93 }
84 94
85 // if content is an image define as a preview too 95 // if content is an image define as a preview too
86 if (in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { 96 if (isset($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
87 $entry->setPreviewPicture($content['url']); 97 $entry->setPreviewPicture($content['url']);
88 } 98 }
89 99
diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
new file mode 100644
index 00000000..c83f9618
--- /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, 0755, true);
40 }
41 }
42
43 /**
44 * Process the html and extract image from it, save them to local and return the updated html.
45 *
46 * @param int $entryId ID of the entry
47 * @param string $html
48 * @param string $url Used as a base path for relative image and folder
49 *
50 * @return string
51 */
52 public function processHtml($entryId, $html, $url)
53 {
54 $crawler = new Crawler($html);
55 $result = $crawler
56 ->filterXpath('//img')
57 ->extract(array('src'));
58
59 $relativePath = $this->getRelativePath($entryId);
60
61 // download and save the image to the folder
62 foreach ($result as $image) {
63 $imagePath = $this->processSingleImage($entryId, $image, $url, $relativePath);
64
65 if (false === $imagePath) {
66 continue;
67 }
68
69 $html = str_replace($image, $imagePath, $html);
70 }
71
72 return $html;
73 }
74
75 /**
76 * Process a single image:
77 * - retrieve it
78 * - re-saved it (for security reason)
79 * - return the new local path.
80 *
81 * @param int $entryId ID of the entry
82 * @param string $imagePath Path to the image to retrieve
83 * @param string $url Url from where the image were found
84 * @param string $relativePath Relative local path to saved the image
85 *
86 * @return string Relative url to access the image from the web
87 */
88 public function processSingleImage($entryId, $imagePath, $url, $relativePath = null)
89 {
90 if (null === $relativePath) {
91 $relativePath = $this->getRelativePath($entryId);
92 }
93
94 $this->logger->debug('DownloadImages: working on image: '.$imagePath);
95
96 $folderPath = $this->baseFolder.'/'.$relativePath;
97
98 // build image path
99 $absolutePath = $this->getAbsoluteLink($url, $imagePath);
100 if (false === $absolutePath) {
101 $this->logger->error('DownloadImages: Can not determine the absolute path for that image, skipping.');
102
103 return false;
104 }
105
106 try {
107 $res = $this->client->get($absolutePath);
108 } catch (\Exception $e) {
109 $this->logger->error('DownloadImages: Can not retrieve image, skipping.', ['exception' => $e]);
110
111 return false;
112 }
113
114 $ext = $this->mimeGuesser->guess($res->getHeader('content-type'));
115 $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]);
116 if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) {
117 $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping '.$imagePath);
118
119 return false;
120 }
121 $hashImage = hash('crc32', $absolutePath);
122 $localPath = $folderPath.'/'.$hashImage.'.'.$ext;
123
124 try {
125 $im = imagecreatefromstring($res->getBody());
126 } catch (\Exception $e) {
127 $im = false;
128 }
129
130 if (false === $im) {
131 $this->logger->error('DownloadImages: Error while regenerating image', ['path' => $localPath]);
132
133 return false;
134 }
135
136 switch ($ext) {
137 case 'gif':
138 imagegif($im, $localPath);
139 $this->logger->debug('DownloadImages: Re-creating gif');
140 break;
141 case 'jpeg':
142 case 'jpg':
143 imagejpeg($im, $localPath, self::REGENERATE_PICTURES_QUALITY);
144 $this->logger->debug('DownloadImages: Re-creating jpg');
145 break;
146 case 'png':
147 imagepng($im, $localPath, ceil(self::REGENERATE_PICTURES_QUALITY / 100 * 9));
148 $this->logger->debug('DownloadImages: Re-creating png');
149 }
150
151 imagedestroy($im);
152
153 return $this->wallabagUrl.'/assets/images/'.$relativePath.'/'.$hashImage.'.'.$ext;
154 }
155
156 /**
157 * Remove all images for the given entry id.
158 *
159 * @param int $entryId ID of the entry
160 */
161 public function removeImages($entryId)
162 {
163 $relativePath = $this->getRelativePath($entryId);
164 $folderPath = $this->baseFolder.'/'.$relativePath;
165
166 $finder = new Finder();
167 $finder
168 ->files()
169 ->ignoreDotFiles(true)
170 ->in($folderPath);
171
172 foreach ($finder as $file) {
173 @unlink($file->getRealPath());
174 }
175
176 @rmdir($folderPath);
177 }
178
179 /**
180 * Generate the folder where we are going to save images based on the entry url.
181 *
182 * @param int $entryId ID of the entry
183 *
184 * @return string
185 */
186 private function getRelativePath($entryId)
187 {
188 $hashId = hash('crc32', $entryId);
189 $relativePath = $hashId[0].'/'.$hashId[1].'/'.$hashId;
190 $folderPath = $this->baseFolder.'/'.$relativePath;
191
192 if (!file_exists($folderPath)) {
193 mkdir($folderPath, 0777, true);
194 }
195
196 $this->logger->debug('DownloadImages: Folder used for that Entry id', ['folder' => $folderPath, 'entryId' => $entryId]);
197
198 return $relativePath;
199 }
200
201 /**
202 * Make an $url absolute based on the $base.
203 *
204 * @see Graby->makeAbsoluteStr
205 *
206 * @param string $base Base url
207 * @param string $url Url to make it absolute
208 *
209 * @return false|string
210 */
211 private function getAbsoluteLink($base, $url)
212 {
213 if (preg_match('!^https?://!i', $url)) {
214 // already absolute
215 return $url;
216 }
217
218 $base = new \SimplePie_IRI($base);
219
220 // remove '//' in URL path (causes URLs not to resolve properly)
221 if (isset($base->ipath)) {
222 $base->ipath = preg_replace('!//+!', '/', $base->ipath);
223 }
224
225 if ($absolute = \SimplePie_IRI::absolutize($base, $url)) {
226 return $absolute->get_uri();
227 }
228
229 $this->logger->error('DownloadImages: Can not make an absolute link', ['base' => $base, 'url' => $url]);
230
231 return false;
232 }
233}
diff --git a/src/Wallabag/CoreBundle/Helper/EntriesExport.php b/src/Wallabag/CoreBundle/Helper/EntriesExport.php
index 4bf292a4..93c01fcb 100644
--- a/src/Wallabag/CoreBundle/Helper/EntriesExport.php
+++ b/src/Wallabag/CoreBundle/Helper/EntriesExport.php
@@ -89,6 +89,11 @@ class EntriesExport
89 throw new \InvalidArgumentException(sprintf('The format "%s" is not yet supported.', $format)); 89 throw new \InvalidArgumentException(sprintf('The format "%s" is not yet supported.', $format));
90 } 90 }
91 91
92 public function exportJsonData()
93 {
94 return $this->prepareSerializingContent('json');
95 }
96
92 /** 97 /**
93 * Use PHPePub to dump a .epub file. 98 * Use PHPePub to dump a .epub file.
94 * 99 *
diff --git a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php
new file mode 100644
index 00000000..8891887b
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php
@@ -0,0 +1,54 @@
1<?php
2
3namespace Wallabag\CoreBundle\Helper;
4
5use Graby\Ring\Client\SafeCurlHandler;
6use GuzzleHttp\Client;
7use GuzzleHttp\Cookie\CookieJar;
8use GuzzleHttp\Event\SubscriberInterface;
9
10/**
11 * Builds and configures the Guzzle HTTP client.
12 */
13class HttpClientFactory
14{
15 /** @var \GuzzleHttp\Event\SubscriberInterface */
16 private $authenticatorSubscriber;
17
18 /** @var \GuzzleHttp\Cookie\CookieJar */
19 private $cookieJar;
20
21 private $restrictedAccess;
22
23 /**
24 * HttpClientFactory constructor.
25 *
26 * @param \GuzzleHttp\Event\SubscriberInterface $authenticatorSubscriber
27 * @param \GuzzleHttp\Cookie\CookieJar $cookieJar
28 * @param string $restrictedAccess this param is a kind of boolean. Values: 0 or 1
29 */
30 public function __construct(SubscriberInterface $authenticatorSubscriber, CookieJar $cookieJar, $restrictedAccess)
31 {
32 $this->authenticatorSubscriber = $authenticatorSubscriber;
33 $this->cookieJar = $cookieJar;
34 $this->restrictedAccess = $restrictedAccess;
35 }
36
37 /**
38 * @return \GuzzleHttp\Client|null
39 */
40 public function buildHttpClient()
41 {
42 if (0 === (int) $this->restrictedAccess) {
43 return null;
44 }
45
46 // we clear the cookie to avoid websites who use cookies for analytics
47 $this->cookieJar->clear();
48 // need to set the (shared) cookie jar
49 $client = new Client(['handler' => new SafeCurlHandler(), 'defaults' => ['cookies' => $this->cookieJar]]);
50 $client->getEmitter()->attach($this->authenticatorSubscriber);
51
52 return $client;
53 }
54}
diff --git a/src/Wallabag/CoreBundle/Helper/Redirect.php b/src/Wallabag/CoreBundle/Helper/Redirect.php
index c14c79d1..f78b7fe0 100644
--- a/src/Wallabag/CoreBundle/Helper/Redirect.php
+++ b/src/Wallabag/CoreBundle/Helper/Redirect.php
@@ -3,6 +3,8 @@
3namespace Wallabag\CoreBundle\Helper; 3namespace Wallabag\CoreBundle\Helper;
4 4
5use Symfony\Component\Routing\Router; 5use Symfony\Component\Routing\Router;
6use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
7use Wallabag\CoreBundle\Entity\Config;
6 8
7/** 9/**
8 * Manage redirections to avoid redirecting to empty routes. 10 * Manage redirections to avoid redirecting to empty routes.
@@ -10,10 +12,12 @@ use Symfony\Component\Routing\Router;
10class Redirect 12class Redirect
11{ 13{
12 private $router; 14 private $router;
15 private $tokenStorage;
13 16
14 public function __construct(Router $router) 17 public function __construct(Router $router, TokenStorageInterface $tokenStorage)
15 { 18 {
16 $this->router = $router; 19 $this->router = $router;
20 $this->tokenStorage = $tokenStorage;
17 } 21 }
18 22
19 /** 23 /**
@@ -24,6 +28,16 @@ class Redirect
24 */ 28 */
25 public function to($url, $fallback = '') 29 public function to($url, $fallback = '')
26 { 30 {
31 $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
32
33 if (null === $user || !is_object($user)) {
34 return $url;
35 }
36
37 if (Config::REDIRECT_TO_HOMEPAGE === $user->getConfig()->getActionMarkAsRead()) {
38 return $this->router->generate('homepage');
39 }
40
27 if (null !== $url) { 41 if (null !== $url) {
28 return $url; 42 return $url;
29 } 43 }
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index 4f03ae0f..b9532fa2 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -20,8 +20,7 @@ class EntryRepository extends EntityRepository
20 private function getBuilderByUser($userId) 20 private function getBuilderByUser($userId)
21 { 21 {
22 return $this->createQueryBuilder('e') 22 return $this->createQueryBuilder('e')
23 ->leftJoin('e.user', 'u') 23 ->andWhere('e.user = :userId')->setParameter('userId', $userId)
24 ->andWhere('u.id = :userId')->setParameter('userId', $userId)
25 ->orderBy('e.createdAt', 'desc') 24 ->orderBy('e.createdAt', 'desc')
26 ; 25 ;
27 } 26 }
@@ -86,6 +85,36 @@ class EntryRepository extends EntityRepository
86 } 85 }
87 86
88 /** 87 /**
88 * Retrieves entries filtered with a search term for a user.
89 *
90 * @param int $userId
91 * @param string $term
92 * @param strint $currentRoute
93 *
94 * @return QueryBuilder
95 */
96 public function getBuilderForSearchByUser($userId, $term, $currentRoute)
97 {
98 $qb = $this
99 ->getBuilderByUser($userId);
100
101 if ('starred' === $currentRoute) {
102 $qb->andWhere('e.isStarred = true');
103 } elseif ('unread' === $currentRoute) {
104 $qb->andWhere('e.isArchived = false');
105 } elseif ('archive' === $currentRoute) {
106 $qb->andWhere('e.isArchived = true');
107 }
108
109 $qb
110 ->andWhere('e.content LIKE :term OR e.title LIKE :term')->setParameter('term', '%'.$term.'%')
111 ->leftJoin('e.tags', 't')
112 ->groupBy('e.id');
113
114 return $qb;
115 }
116
117 /**
89 * Retrieves untagged entries for a user. 118 * Retrieves untagged entries for a user.
90 * 119 *
91 * @param int $userId 120 * @param int $userId
@@ -96,9 +125,7 @@ class EntryRepository extends EntityRepository
96 { 125 {
97 return $this 126 return $this
98 ->getBuilderByUser($userId) 127 ->getBuilderByUser($userId)
99 ->leftJoin('e.tags', 't') 128 ->andWhere('size(e.tags) = 0');
100 ->groupBy('e.id')
101 ->having('count(t.id) = 0');
102 } 129 }
103 130
104 /** 131 /**
@@ -144,7 +171,7 @@ class EntryRepository extends EntityRepository
144 $qb->orderBy('e.updatedAt', $order); 171 $qb->orderBy('e.updatedAt', $order);
145 } 172 }
146 173
147 $pagerAdapter = new DoctrineORMAdapter($qb); 174 $pagerAdapter = new DoctrineORMAdapter($qb, true, false);
148 175
149 return new Pagerfanta($pagerAdapter); 176 return new Pagerfanta($pagerAdapter);
150 } 177 }
@@ -329,4 +356,18 @@ class EntryRepository extends EntityRepository
329 356
330 return $qb->getQuery()->getSingleScalarResult(); 357 return $qb->getQuery()->getSingleScalarResult();
331 } 358 }
359
360 /**
361 * Remove all entries for a user id.
362 * Used when a user want to reset all informations.
363 *
364 * @param int $userId
365 */
366 public function removeAllByUserId($userId)
367 {
368 $this->getEntityManager()
369 ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId')
370 ->setParameter('userId', $userId)
371 ->execute();
372 }
332} 373}
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php
index e76878d4..2182df25 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,21 @@ 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') 52 ->orderBy('t.slug')
50 ->addGroupBy('t.id')
51 ->getQuery() 53 ->getQuery()
52 ->getArrayResult(); 54 ->getArrayResult();
55
56 $tags = [];
57 foreach ($ids as $id) {
58 $tags[] = $this->find($id);
59 }
60
61 return $tags;
53 } 62 }
54 63
55 /** 64 /**
diff --git a/src/Wallabag/CoreBundle/Resources/config/parameters.yml b/src/Wallabag/CoreBundle/Resources/config/parameters.yml
index 6068e84c..4948f385 100644
--- a/src/Wallabag/CoreBundle/Resources/config/parameters.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/parameters.yml
@@ -1,8 +1,9 @@
1parameters: 1parameters:
2 addons_url: 2 addons_url:
3 firefox: https://addons.mozilla.org/firefox/addon/wallabag-v2/ 3 firefox: https://addons.mozilla.org/firefox/addon/wallabagger/
4 chrome: https://chrome.google.com/webstore/detail/wallabagit/peehlcgckcnclnjlndmoddifcicdnabm 4 chrome: https://chrome.google.com/webstore/detail/wallabagger/gbmgphmejlcoihgedabhgjdkcahacjlj
5 opera: https://addons.opera.com/en/extensions/details/wallabagger/?display=en
5 f_droid: https://f-droid.org/app/fr.gaulupeau.apps.InThePoche 6 f_droid: https://f-droid.org/app/fr.gaulupeau.apps.InThePoche
6 google_play: https://play.google.com/store/apps/details?id=fr.gaulupeau.apps.InThePoche 7 google_play: https://play.google.com/store/apps/details?id=fr.gaulupeau.apps.InThePoche
7 ios: https://itunes.apple.com/app/wallabag/id828331015?mt=8 8 ios: https://itunes.apple.com/app/wallabag-2/id1170800946?mt=8
8 windows: https://www.microsoft.com/store/apps/wallabag/9nblggh11646 9 windows: https://www.microsoft.com/store/apps/wallabag/9nblggh11646
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index ed66d2be..fadd5e49 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:
@@ -41,11 +41,44 @@ services:
41 arguments: 41 arguments:
42 - 42 -
43 error_message: '%wallabag_core.fetching_error_message%' 43 error_message: '%wallabag_core.fetching_error_message%'
44 - "@wallabag_core.guzzle.http_client"
45 - "@wallabag_core.graby.config_builder"
44 calls: 46 calls:
45 - [ setLogger, [ "@logger" ] ] 47 - [ setLogger, [ "@logger" ] ]
46 tags: 48 tags:
47 - { name: monolog.logger, channel: graby } 49 - { name: monolog.logger, channel: graby }
48 50
51 wallabag_core.graby.config_builder:
52 class: Graby\SiteConfig\ConfigBuilder
53 arguments:
54 - {}
55 - "@logger"
56
57 wallabag_core.guzzle.http_client:
58 class: GuzzleHttp\ClientInterface
59 factory: ["@wallabag_core.guzzle.http_client_factory", buildHttpClient]
60
61 wallabag_core.guzzle_authenticator.config_builder:
62 class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder
63 arguments:
64 - "@wallabag_core.graby.config_builder"
65 - "%sites_credentials%"
66
67 # service alias override
68 bd_guzzle_site_authenticator.site_config_builder:
69 alias: wallabag_core.guzzle_authenticator.config_builder
70
71 wallabag_core.guzzle.http_client_factory:
72 class: Wallabag\CoreBundle\Helper\HttpClientFactory
73 arguments:
74 - "@bd_guzzle_site_authenticator.authenticator_subscriber"
75 - "@wallabag_core.guzzle.cookie_jar"
76 - '@=service(''craue_config'').get(''restricted_access'')'
77
78 wallabag_core.guzzle.cookie_jar:
79 class: GuzzleHttp\Cookie\FileCookieJar
80 arguments: ["%kernel.cache_dir%/cookiejar.json"]
81
49 wallabag_core.content_proxy: 82 wallabag_core.content_proxy:
50 class: Wallabag\CoreBundle\Helper\ContentProxy 83 class: Wallabag\CoreBundle\Helper\ContentProxy
51 arguments: 84 arguments:
@@ -53,6 +86,7 @@ services:
53 - "@wallabag_core.rule_based_tagger" 86 - "@wallabag_core.rule_based_tagger"
54 - "@wallabag_core.tag_repository" 87 - "@wallabag_core.tag_repository"
55 - "@logger" 88 - "@logger"
89 - '%wallabag_core.fetching_error_message%'
56 90
57 wallabag_core.rule_based_tagger: 91 wallabag_core.rule_based_tagger:
58 class: Wallabag\CoreBundle\Helper\RuleBasedTagger 92 class: Wallabag\CoreBundle\Helper\RuleBasedTagger
@@ -94,6 +128,7 @@ services:
94 class: Wallabag\CoreBundle\Helper\Redirect 128 class: Wallabag\CoreBundle\Helper\Redirect
95 arguments: 129 arguments:
96 - "@router" 130 - "@router"
131 - "@security.token_storage"
97 132
98 wallabag_core.helper.prepare_pager_for_entries: 133 wallabag_core.helper.prepare_pager_for_entries:
99 class: Wallabag\CoreBundle\Helper\PreparePagerForEntries 134 class: Wallabag\CoreBundle\Helper\PreparePagerForEntries
@@ -109,9 +144,38 @@ services:
109 host: '%redis_host%' 144 host: '%redis_host%'
110 port: '%redis_port%' 145 port: '%redis_port%'
111 path: '%redis_path%' 146 path: '%redis_path%'
147 password: '%redis_password%'
112 148
113 wallabag_core.exception_controller: 149 wallabag_core.exception_controller:
114 class: Wallabag\CoreBundle\Controller\ExceptionController 150 class: Wallabag\CoreBundle\Controller\ExceptionController
115 arguments: 151 arguments:
116 - '@twig' 152 - '@twig'
117 - '%kernel.debug%' 153 - '%kernel.debug%'
154
155 wallabag_core.subscriber.sqlite_cascade_delete:
156 class: Wallabag\CoreBundle\Event\Subscriber\SQLiteCascadeDeleteSubscriber
157 arguments:
158 - "@doctrine"
159 tags:
160 - { name: doctrine.event_subscriber }
161
162 wallabag_core.subscriber.download_images:
163 class: Wallabag\CoreBundle\Event\Subscriber\DownloadImagesSubscriber
164 arguments:
165 - "@doctrine.orm.default_entity_manager"
166 - "@wallabag_core.entry.download_images"
167 - '@=service(''craue_config'').get(''download_images_enabled'')'
168 - "@logger"
169 tags:
170 - { name: kernel.event_subscriber }
171
172 wallabag_core.entry.download_images:
173 class: Wallabag\CoreBundle\Helper\DownloadImages
174 arguments:
175 - "@wallabag_core.entry.download_images.client"
176 - "%kernel.root_dir%/../web/assets/images"
177 - '@=service(''craue_config'').get(''wallabag_url'')'
178 - "@logger"
179
180 wallabag_core.entry.download_images.client:
181 class: GuzzleHttp\Client
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index a1bee173..34d4021c 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -70,7 +70,12 @@ config:
70 # 200_word: 'I read ~200 words per minute' 70 # 200_word: 'I read ~200 words per minute'
71 # 300_word: 'I read ~300 words per minute' 71 # 300_word: 'I read ~300 words per minute'
72 # 400_word: 'I read ~400 words per minute' 72 # 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer 77 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Emailadresse' 99 email_label: 'Emailadresse'
95 # twoFactorAuthentication_label: 'Two factor authentication' 100 # twoFactorAuthentication_label: 'Two factor authentication'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Gammel adgangskode' 116 old_password_label: 'Gammel adgangskode'
@@ -145,15 +162,16 @@ entry:
145 # archived: 'Archived entries' 162 # archived: 'Archived entries'
146 # filtered: 'Filtered entries' 163 # filtered: 'Filtered entries'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' 168 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
151 reading_time: 'estimeret læsetid' 169 reading_time: 'estimeret læsetid'
152 reading_time_minutes: 'estimeret læsetid: %readingTime% min' 170 reading_time_minutes: 'estimeret læsetid: %readingTime% min'
153 reading_time_less_one_minute: 'estimeret læsetid: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'estimeret læsetid: &lt; 1 min'
154 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'Marker som læst' 176 toogle_as_read: 'Marker som læst'
159 toogle_as_star: 'Skift favoritstatus' 177 toogle_as_star: 'Skift favoritstatus'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Har et vist billede' 186 preview_picture_label: 'Har et vist billede'
169 preview_picture_help: 'Forhåndsvis billede' 187 preview_picture_help: 'Forhåndsvis billede'
170 language_label: 'Sprog' 188 language_label: 'Sprog'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Læsetid i minutter' 191 label: 'Læsetid i minutter'
173 from: 'fra' 192 from: 'fra'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 # page_title: 'Edit an entry' 234 # page_title: 'Edit an entry'
214 # title_label: 'Title' 235 # title_label: 'Title'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'How-to' 274 page_title: 'How-to'
254 # page_description: 'There are several ways to save an article:' 275 # page_description: 'There are several ways to save an article:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'Browserudvidelser' 280 browser_addons: 'Browserudvidelser'
257 mobile_apps: 'Apps' 281 mobile_apps: 'Apps'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Standardudvidelse til Firefox' 286 firefox: 'Standardudvidelse til Firefox'
263 chrome: 'Chrome-udvidelse' 287 chrome: 'Chrome-udvidelse'
288 opera: 'Opera-udvidelse'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,6 +294,33 @@ howto:
269 # windows: 'on the Microsoft Store' 294 # windows: 'on the Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Træk dette link til din bogmærkeliste:' 296 description: 'Træk dette link til din bogmærkeliste:'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 # page_title: 'Quickstart' 326 # page_title: 'Quickstart'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 382 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
331 # see_untagged_entries: 'See untagged entries' 383 # see_untagged_entries: 'See untagged entries'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 # page_title: 'Import' 389 # page_title: 'Import'
@@ -362,6 +417,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 418 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 # 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:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 # firefox: 421 # firefox:
366 # page_title: 'Import > Firefox' 422 # page_title: 'Import > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +430,10 @@ import:
374 # page_title: 'Import > Instapaper' 430 # page_title: 'Import > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 # page_title: "Import > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 # page_title: 'Developer' 439 # page_title: 'Developer'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'Emailadresse' 505 email_label: 'Emailadresse'
446 # enabled_label: 'Enabled' 506 # enabled_label: 'Enabled'
447 # locked_label: 'Locked'
448 # last_login_label: 'Last login' 507 # last_login_label: 'Last login'
449 # twofactor_label: Two factor authentication 508 # twofactor_label: Two factor authentication
450 # save: Save 509 # save: Save
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'RSS-oplysninger opdateret' 524 rss_updated: 'RSS-oplysninger opdateret'
466 # tagging_rules_updated: 'Tagging rules updated' 525 # tagging_rules_updated: 'Tagging rules updated'
467 # tagging_rules_deleted: 'Tagging rule deleted' 526 # tagging_rules_deleted: 'Tagging rule deleted'
468 # user_added: 'User "%username%" added'
469 # rss_token_updated: 'RSS token updated' 527 # rss_token_updated: 'RSS token updated'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
470 entry: 531 entry:
471 notice: 532 notice:
472 # entry_already_saved: 'Entry already saved on %date%' 533 # entry_already_saved: 'Entry already saved on %date%'
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 # client_created: 'New client created.' 558 # client_created: 'New client created.'
498 # client_deleted: 'Client deleted' 559 # client_deleted: 'Client deleted'
560 user:
561 notice:
562 # added: 'User "%username%" added'
563 # updated: 'User "%username%" updated'
564 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
index c9625d06..eb3644f6 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Ich lese ~200 Wörter pro Minute' 70 200_word: 'Ich lese ~200 Wörter pro Minute'
71 300_word: 'Ich lese ~300 Wörter pro Minute' 71 300_word: 'Ich lese ~300 Wörter pro Minute'
72 400_word: 'Ich lese ~400 Wörter pro Minute' 72 400_word: 'Ich lese ~400 Wörter pro Minute'
73 action_mark_as_read:
74 label: 'Wohin soll nach dem Gelesenmarkieren eines Artikels weitergeleitet werden?'
75 redirect_homepage: 'Zur Homepage'
76 redirect_current_page: 'Zur aktuellen Seite'
73 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren 77 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren
78 android_configuration: Konfiguriere deine Android Application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-Mail-Adresse' 99 email_label: 'E-Mail-Adresse'
95 twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' 100 twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Lösche mein Konto (a.k.a Gefahrenzone)
104 description: Wenn du dein Konto löschst, werden ALL deine Artikel, ALL deine Tags, ALL deine Anmerkungen und dein Konto dauerhaft gelöscht (kann NICHT RÜCKGÄNGIG gemacht werden). Du wirst anschließend ausgeloggt.
105 confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN)
106 button: Lösche mein Konto
107 reset:
108 title: Zurücksetzen (a.k.a Gefahrenzone)
109 description: Beim Nutzen der folgenden Schaltflächenhast du die Möglichkeit, einige Informationen von deinem Konto zu entfernen. Sei dir bewusst, dass dies NICHT RÜCKGÄNGIG zu machen ist.
110 annotations: Entferne ALLE Annotationen
111 tags: Entferne ALLE Tags
112 entries: Entferne ALLE Einträge
113 confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Altes Kennwort' 116 old_password_label: 'Altes Kennwort'
@@ -145,15 +162,16 @@ entry:
145 archived: 'Archivierte Einträge' 162 archived: 'Archivierte Einträge'
146 filtered: 'Gefilterte Einträge' 163 filtered: 'Gefilterte Einträge'
147 filtered_tags: 'Gefiltert nach Tags:' 164 filtered_tags: 'Gefiltert nach Tags:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Nicht getaggte Einträge' 166 untagged: 'Nicht getaggte Einträge'
149 list: 167 list:
150 number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.' 168 number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.'
151 reading_time: 'geschätzte Lesezeit' 169 reading_time: 'geschätzte Lesezeit'
152 reading_time_minutes: 'geschätzte Lesezeit: %readingTime% min' 170 reading_time_minutes: 'geschätzte Lesezeit: %readingTime% min'
153 reading_time_less_one_minute: 'geschätzte Lesezeit: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'geschätzte Lesezeit: &lt; 1 min'
154 number_of_tags: '{1}und ein anderer Tag|]1,Inf[und %count% andere Tags' 172 number_of_tags: '{1}und ein anderer Tag|]1,Inf[und %count% andere Tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'Original' 175 original_article: 'Original'
158 toogle_as_read: 'Gelesen-Status ändern' 176 toogle_as_read: 'Gelesen-Status ändern'
159 toogle_as_star: 'Favoriten-Status ändern' 177 toogle_as_star: 'Favoriten-Status ändern'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Vorschaubild vorhanden' 186 preview_picture_label: 'Vorschaubild vorhanden'
169 preview_picture_help: 'Vorschaubild' 187 preview_picture_help: 'Vorschaubild'
170 language_label: 'Sprache' 188 language_label: 'Sprache'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Lesezeit in Minuten' 191 label: 'Lesezeit in Minuten'
173 from: 'von' 192 from: 'von'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'https://website.de' 228 placeholder: 'https://website.de'
210 form_new: 229 form_new:
211 url_label: URL 230 url_label: URL
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Eintrag bearbeiten' 234 page_title: 'Eintrag bearbeiten'
214 title_label: 'Titel' 235 title_label: 'Titel'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'How-To' 274 page_title: 'How-To'
254 page_description: 'Es gibt mehrere Möglichkeiten, einen Artikel zu speichern:' 275 page_description: 'Es gibt mehrere Möglichkeiten, einen Artikel zu speichern:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'Browser-Erweiterungen' 280 browser_addons: 'Browser-Erweiterungen'
257 mobile_apps: 'Apps' 281 mobile_apps: 'Apps'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Firefox-Erweiterung' 286 firefox: 'Firefox-Erweiterung'
263 chrome: 'Chrome-Erweiterung' 287 chrome: 'Chrome-Erweiterung'
288 opera: 'Opera-Erweiterung'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'im Microsoft-Store' 294 windows: 'im Microsoft-Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Ziehe diesen Link in deine Lesezeichenleiste:' 296 description: 'Ziehe diesen Link in deine Lesezeichenleiste:'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Schnelleinstieg' 326 page_title: 'Schnelleinstieg'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: '{0} Es gibt keine Tags.|{1} Es gibt einen Tag.|]1,Inf[ Es gibt %count% Tags.' 382 number_on_the_page: '{0} Es gibt keine Tags.|{1} Es gibt einen Tag.|]1,Inf[ Es gibt %count% Tags.'
331 see_untagged_entries: 'Zeige nicht getaggte Einträge' 383 see_untagged_entries: 'Zeige nicht getaggte Einträge'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 page_title: 'Importieren' 389 page_title: 'Importieren'
@@ -362,6 +417,7 @@ import:
362 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.' 417 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.'
363 worker: 418 worker:
364 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:" 419 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:"
420 download_images_warning: "Du hast das Herunterladen von Bildern für deine Artikel aktiviert. Verbunden mit dem klassischen Import kann es ewig dauern fortzufahren (oder sogar fehlschlagen). Wir <strong>empfehlen</strong> den asynchronen Import zu aktivieren, um Fehler zu vermeiden."
365 firefox: 421 firefox:
366 page_title: 'Aus Firefox importieren' 422 page_title: 'Aus Firefox importieren'
367 description: "Dieser Import wird all deine Lesezeichen aus Firefox importieren. Gehe zu deinen Lesezeichen (Strg+Shift+O), dann auf \"Importen und Sichern\", wähle \"Sichern…\". Du erhälst eine .json Datei." 423 description: "Dieser Import wird all deine Lesezeichen aus Firefox importieren. Gehe zu deinen Lesezeichen (Strg+Shift+O), dann auf \"Importen und Sichern\", wähle \"Sichern…\". Du erhälst eine .json Datei."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Aus Instapaper importieren' 430 page_title: 'Aus Instapaper importieren'
375 description: 'Dieser Import wird all deine Instapaper Artikel importieren. Auf der Einstellungsseite (https://www.instapaper.com/user) klickst du auf "Download .CSV Datei" in dem Abschnitt "Export". Eine CSV Datei wird heruntergeladen (z.B. "instapaper-export.csv").' 431 description: 'Dieser Import wird all deine Instapaper Artikel importieren. Auf der Einstellungsseite (https://www.instapaper.com/user) klickst du auf "Download .CSV Datei" in dem Abschnitt "Export". Eine CSV Datei wird heruntergeladen (z.B. "instapaper-export.csv").'
376 how_to: "Bitte wähle deine Instapaper Sicherungsdatei aus und klicke den nachfolgenden Button zum Importieren." 432 how_to: "Bitte wähle deine Instapaper Sicherungsdatei aus und klicke den nachfolgenden Button zum Importieren."
433 pinboard:
434 page_title: "Aus Pinboard importieren"
435 description: 'Dieser Import wird all deine Pinboard Artikel importieren. Auf der Seite Backup (https://pinboard.in/settings/backup) klickst du auf "JSON" in dem Abschnitt "Lesezeichen". Eine JSON Datei wird dann heruntergeladen (z.B. "pinboard_export").'
436 how_to: 'Bitte wähle deinen Pinboard Export aus und klicke den nachfolgenden Button zum Importieren.'
377 437
378developer: 438developer:
379 page_title: 'Entwickler' 439 page_title: 'Entwickler'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'E-Mail-Adresse' 505 email_label: 'E-Mail-Adresse'
446 enabled_label: 'Aktiviert' 506 enabled_label: 'Aktiviert'
447 locked_label: 'Gesperrt'
448 last_login_label: 'Letzter Login' 507 last_login_label: 'Letzter Login'
449 twofactor_label: Zwei-Faktor-Authentifizierung 508 twofactor_label: Zwei-Faktor-Authentifizierung
450 save: Speichern 509 save: Speichern
@@ -453,7 +512,7 @@ user:
453 back_to_list: Zurück zur Liste 512 back_to_list: Zurück zur Liste
454 513
455error: 514error:
456 # page_title: An error occurred 515 page_title: Ein Fehler ist aufgetreten
457 516
458flashes: 517flashes:
459 config: 518 config:
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'RSS-Informationen aktualisiert' 524 rss_updated: 'RSS-Informationen aktualisiert'
466 tagging_rules_updated: 'Tagging-Regeln aktualisiert' 525 tagging_rules_updated: 'Tagging-Regeln aktualisiert'
467 tagging_rules_deleted: 'Tagging-Regel gelöscht' 526 tagging_rules_deleted: 'Tagging-Regel gelöscht'
468 user_added: 'Benutzer "%username%" erstellt'
469 rss_token_updated: 'RSS-Token aktualisiert' 527 rss_token_updated: 'RSS-Token aktualisiert'
528 annotations_reset: Anmerkungen zurücksetzen
529 tags_reset: Tags zurücksetzen
530 entries_reset: Einträge zurücksetzen
470 entry: 531 entry:
471 notice: 532 notice:
472 entry_already_saved: 'Eintrag bereits am %date% gespeichert' 533 entry_already_saved: 'Eintrag bereits am %date% gespeichert'
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 client_created: 'Neuer Client erstellt.' 558 client_created: 'Neuer Client erstellt.'
498 client_deleted: 'Client gelöscht' 559 client_deleted: 'Client gelöscht'
560 user:
561 notice:
562 added: 'Benutzer "%username%" hinzugefügt'
563 updated: 'Benutzer "%username%" aktualisiert'
564 deleted: 'Benutzer "%username%" gelöscht'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index 139cdc24..2626523a 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'I read ~200 words per minute' 70 200_word: 'I read ~200 words per minute'
71 300_word: 'I read ~300 words per minute' 71 300_word: 'I read ~300 words per minute'
72 400_word: 'I read ~400 words per minute' 72 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 label: 'Where do you want to be redirected after mark an article as read?'
75 redirect_homepage: 'To the homepage'
76 redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Consumer key for Pocket to import contents 77 pocket_consumer_key_label: Consumer key for Pocket to import contents
78 android_configuration: Configure your Android application
74 help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 help_items_per_page: "You can change the number of articles displayed on each page." 80 help_items_per_page: "You can change the number of articles displayed on each page."
76 help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Email' 99 email_label: 'Email'
95 twoFactorAuthentication_label: 'Two factor authentication' 100 twoFactorAuthentication_label: 'Two factor authentication'
96 help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Delete my account (a.k.a danger zone)
104 description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 button: Delete my account
107 reset:
108 title: Reset area (a.k.a danger zone)
109 description: By hitting buttons below you'll have ability to remove some information from your account. Be aware that these actions are IRREVERSIBLE.
110 annotations: Remove ALL annotations
111 tags: Remove ALL tags
112 entries: Remove ALL entries
113 confirm: Are you really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 description: "You can change your password here. Your new password should by at least 8 characters long." 115 description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Current password' 116 old_password_label: 'Current password'
@@ -145,15 +162,16 @@ entry:
145 archived: 'Archived entries' 162 archived: 'Archived entries'
146 filtered: 'Filtered entries' 163 filtered: 'Filtered entries'
147 filtered_tags: 'Filtered by tags:' 164 filtered_tags: 'Filtered by tags:'
165 filtered_search: 'Filtered by search:'
148 untagged: 'Untagged entries' 166 untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.' 168 number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
151 reading_time: 'estimated reading time' 169 reading_time: 'estimated reading time'
152 reading_time_minutes: 'estimated reading time: %readingTime% min' 170 reading_time_minutes: 'estimated reading time: %readingTime% min'
153 reading_time_less_one_minute: 'estimated reading time: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'estimated reading time: &lt; 1 min'
154 number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'Toggle mark as read' 176 toogle_as_read: 'Toggle mark as read'
159 toogle_as_star: 'Toggle starred' 177 toogle_as_star: 'Toggle starred'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Has a preview picture' 186 preview_picture_label: 'Has a preview picture'
169 preview_picture_help: 'Preview picture' 187 preview_picture_help: 'Preview picture'
170 language_label: 'Language' 188 language_label: 'Language'
189 http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Reading time in minutes' 191 label: 'Reading time in minutes'
173 from: 'from' 192 from: 'from'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Edit an entry' 234 page_title: 'Edit an entry'
214 title_label: 'Title' 235 title_label: 'Title'
@@ -251,6 +272,9 @@ about:
251 272
252howto: 273howto:
253 page_title: 'How to' 274 page_title: 'How to'
275 tab_menu:
276 add_link: "Add a link"
277 shortcuts: "Use shortcuts"
254 page_description: 'There are several ways to save an article:' 278 page_description: 'There are several ways to save an article:'
255 top_menu: 279 top_menu:
256 browser_addons: 'Browser addons' 280 browser_addons: 'Browser addons'
@@ -259,8 +283,9 @@ howto:
259 form: 283 form:
260 description: 'Thanks to this form' 284 description: 'Thanks to this form'
261 browser_addons: 285 browser_addons:
262 firefox: 'Standard Firefox Add-On' 286 firefox: 'Firefox addon'
263 chrome: 'Chrome Extension' 287 chrome: 'Chrome addon'
288 opera: 'Opera addon'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'on the Microsoft Store' 294 windows: 'on the Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Drag & drop this link to your bookmarks bar:' 296 description: 'Drag & drop this link to your bookmarks bar:'
297 shortcuts:
298 page_description: Here are the shortcuts available in wallabag.
299 shortcut: Shortcut
300 action: Action
301 all_pages_title: Shortcuts available in all pages
302 go_unread: Go to unread
303 go_starred: Go to starred
304 go_archive: Go to archive
305 go_all: Go to all entries
306 go_tags: Go to tags
307 go_config: Go to config
308 go_import: Go to import
309 go_developers: Go to developers
310 go_howto: Go to howto (this page!)
311 go_logout: Logout
312 list_title: Shortcuts available in listing pages
313 search: Display the search form
314 article_title: Shortcuts available in entry view
315 open_original: Open original URL of the entry
316 toggle_favorite: Toggle star status for the entry
317 toggle_archive: Toggle read status for the entry
318 delete: Delete the entry
319 material_title: Shortcuts available with Material theme only
320 add_link: Add a new link
321 hide_form: Hide the current form (search or new link)
322 arrows_navigation: Navigate through articles
323 open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Quickstart' 326 page_title: 'Quickstart'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: '{0} There are no tags.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 382 number_on_the_page: '{0} There are no tags.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
331 see_untagged_entries: 'See untagged entries' 383 see_untagged_entries: 'See untagged entries'
384 new:
385 add: 'Add'
386 placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 page_title: 'Import' 389 page_title: 'Import'
@@ -362,6 +417,7 @@ import:
362 how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 418 worker:
364 enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 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:"
420 download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 421 firefox:
366 page_title: 'Import > Firefox' 422 page_title: 'Import > Firefox'
367 description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Import > Instapaper' 430 page_title: 'Import > Instapaper'
375 description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 page_title: "Import > Pinboard"
435 description: 'This importer will import all your Pinboard articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 page_title: 'Developer' 439 page_title: 'Developer'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'Email' 505 email_label: 'Email'
446 enabled_label: 'Enabled' 506 enabled_label: 'Enabled'
447 locked_label: 'Locked'
448 last_login_label: 'Last login' 507 last_login_label: 'Last login'
449 twofactor_label: Two factor authentication 508 twofactor_label: Two factor authentication
450 save: Save 509 save: Save
@@ -466,6 +525,9 @@ flashes:
466 tagging_rules_updated: 'Tagging rules updated' 525 tagging_rules_updated: 'Tagging rules updated'
467 tagging_rules_deleted: 'Tagging rule deleted' 526 tagging_rules_deleted: 'Tagging rule deleted'
468 rss_token_updated: 'RSS token updated' 527 rss_token_updated: 'RSS token updated'
528 annotations_reset: Annotations reset
529 tags_reset: Tags reset
530 entries_reset: Entries reset
469 entry: 531 entry:
470 notice: 532 notice:
471 entry_already_saved: 'Entry already saved on %date%' 533 entry_already_saved: 'Entry already saved on %date%'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
index 70e64bec..d45f8bf5 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Leo ~200 palabras por minuto' 70 200_word: 'Leo ~200 palabras por minuto'
71 300_word: 'Leo ~300 palabras por minuto' 71 300_word: 'Leo ~300 palabras por minuto'
72 400_word: 'Leo ~400 palabras por minuto' 72 400_word: 'Leo ~400 palabras por minuto'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 # pocket_consumer_key_label: Consumer key for Pocket to import contents 77 # pocket_consumer_key_label: Consumer key for Pocket to import contents
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Direccion e-mail' 99 email_label: 'Direccion e-mail'
95 twoFactorAuthentication_label: 'Autentificación de dos factores' 100 twoFactorAuthentication_label: 'Autentificación de dos factores'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Contraseña actual' 116 old_password_label: 'Contraseña actual'
@@ -145,15 +162,16 @@ entry:
145 archived: 'Artículos archivados' 162 archived: 'Artículos archivados'
146 filtered: 'Artículos filtrados' 163 filtered: 'Artículos filtrados'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.' 168 number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.'
151 reading_time: 'tiempo estimado de lectura' 169 reading_time: 'tiempo estimado de lectura'
152 reading_time_minutes: 'tiempo estimado de lectura: %readingTime% min' 170 reading_time_minutes: 'tiempo estimado de lectura: %readingTime% min'
153 reading_time_less_one_minute: 'tiempo estimado de lectura: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'tiempo estimado de lectura: &lt; 1 min'
154 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'Marcar como leído/ no leído' 176 toogle_as_read: 'Marcar como leído/ no leído'
159 toogle_as_star: 'Marcar como favorito/ no favorito' 177 toogle_as_star: 'Marcar como favorito/ no favorito'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Hay una foto' 186 preview_picture_label: 'Hay una foto'
169 preview_picture_help: 'Foto de preview' 187 preview_picture_help: 'Foto de preview'
170 language_label: 'Idioma' 188 language_label: 'Idioma'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Duración de lectura en minutos' 191 label: 'Duración de lectura en minutos'
173 from: 'de' 192 from: 'de'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Editar un artículo' 234 page_title: 'Editar un artículo'
214 title_label: 'Título' 235 title_label: 'Título'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'Ayuda' 274 page_title: 'Ayuda'
254 page_description: 'Hay muchas maneras para guardar un artículo:' 275 page_description: 'Hay muchas maneras para guardar un artículo:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'Extensiones de navigador' 280 browser_addons: 'Extensiones de navigador'
257 mobile_apps: 'Aplicaciones para smartphone' 281 mobile_apps: 'Aplicaciones para smartphone'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Extensión Firefox' 286 firefox: 'Extensión Firefox'
263 chrome: 'Extensión Chrome' 287 chrome: 'Extensión Chrome'
288 opera: 'Extensión Opera'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'por la tienda de Microsoft' 294 windows: 'por la tienda de Microsoft'
270 bookmarklet: 295 bookmarklet:
271 description: 'Desplazar y soltar este link en la barra de marcadores :' 296 description: 'Desplazar y soltar este link en la barra de marcadores :'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Comienzo rápido' 326 page_title: 'Comienzo rápido'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: '{0} No hay ninguna etiqueta.|{1} Hay una etiqueta.|]1,Inf[ Hay %count% etiquetas.' 382 number_on_the_page: '{0} No hay ninguna etiqueta.|{1} Hay una etiqueta.|]1,Inf[ Hay %count% etiquetas.'
331 # see_untagged_entries: 'See untagged entries' 383 # see_untagged_entries: 'See untagged entries'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 page_title: 'Importar' 389 page_title: 'Importar'
@@ -362,6 +417,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 418 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 # 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:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 421 firefox:
366 page_title: 'Importar > Firefox' 422 page_title: 'Importar > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Importar > Instapaper' 430 page_title: 'Importar > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 page_title: "Importar > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 page_title: 'Promotor' 439 page_title: 'Promotor'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'Email' 505 email_label: 'Email'
446 # enabled_label: 'Enabled' 506 # enabled_label: 'Enabled'
447 # locked_label: 'Locked'
448 # last_login_label: 'Last login' 507 # last_login_label: 'Last login'
449 # twofactor_label: Two factor authentication 508 # twofactor_label: Two factor authentication
450 # save: Save 509 # save: Save
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'La configuración de los feeds RSS ha sido actualizada' 524 rss_updated: 'La configuración de los feeds RSS ha sido actualizada'
466 tagging_rules_updated: 'Regla de etiquetado borrada' 525 tagging_rules_updated: 'Regla de etiquetado borrada'
467 tagging_rules_deleted: 'Regla de etiquetado actualizada' 526 tagging_rules_deleted: 'Regla de etiquetado actualizada'
468 user_added: 'Usuario "%username%" añadido'
469 rss_token_updated: 'RSS token actualizado' 527 rss_token_updated: 'RSS token actualizado'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
470 entry: 531 entry:
471 notice: 532 notice:
472 entry_already_saved: 'Entrada ya guardada por %fecha%' 533 entry_already_saved: 'Entrada ya guardada por %fecha%'
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 client_created: 'Nuevo cliente creado.' 558 client_created: 'Nuevo cliente creado.'
498 client_deleted: 'Cliente suprimido' 559 client_deleted: 'Cliente suprimido'
560 user:
561 notice:
562 # added: 'User "%username%" added'
563 # updated: 'User "%username%" updated'
564 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
index 75359901..36a7cd80 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'من تقریباً ۲۰۰ واژه را در دقیقه می‌خوانم' 70 200_word: 'من تقریباً ۲۰۰ واژه را در دقیقه می‌خوانم'
71 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم' 71 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم'
72 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم' 72 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب 77 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'نشانی ایمیل' 99 email_label: 'نشانی ایمیل'
95 twoFactorAuthentication_label: 'تأیید ۲مرحله‌ای' 100 twoFactorAuthentication_label: 'تأیید ۲مرحله‌ای'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'رمز قدیمی' 116 old_password_label: 'رمز قدیمی'
@@ -145,15 +162,16 @@ entry:
145 archived: 'مقاله‌های بایگانی‌شده' 162 archived: 'مقاله‌های بایگانی‌شده'
146 filtered: 'مقاله‌های فیلترشده' 163 filtered: 'مقاله‌های فیلترشده'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: '{0} هیج مقاله‌ای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.' 168 number_on_the_page: '{0} هیج مقاله‌ای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.'
151 reading_time: 'زمان تخمینی برای خواندن' 169 reading_time: 'زمان تخمینی برای خواندن'
152 reading_time_minutes: 'زمان تخمینی برای خواندن: %readingTime% min' 170 reading_time_minutes: 'زمان تخمینی برای خواندن: %readingTime% min'
153 reading_time_less_one_minute: 'زمان تخمینی برای خواندن: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'زمان تخمینی برای خواندن: &lt; 1 min'
154 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'خوانده‌شده/خوانده‌نشده' 176 toogle_as_read: 'خوانده‌شده/خوانده‌نشده'
159 toogle_as_star: 'برگزیده/نابرگزیده' 177 toogle_as_star: 'برگزیده/نابرگزیده'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'دارای عکس پیش‌نمایش' 186 preview_picture_label: 'دارای عکس پیش‌نمایش'
169 preview_picture_help: 'پیش‌نمایش عکس' 187 preview_picture_help: 'پیش‌نمایش عکس'
170 language_label: 'زبان' 188 language_label: 'زبان'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'زمان خواندن به دقیقه' 191 label: 'زمان خواندن به دقیقه'
173 from: 'از' 192 from: 'از'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: نشانی 230 url_label: نشانی
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'ویرایش مقاله' 234 page_title: 'ویرایش مقاله'
214 title_label: 'عنوان' 235 title_label: 'عنوان'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'خودآموز' 274 page_title: 'خودآموز'
254 page_description: 'راه‌های زیادی برای ذخیرهٔ مقاله‌ها هست:' 275 page_description: 'راه‌های زیادی برای ذخیرهٔ مقاله‌ها هست:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'افزونه برای مرورگرها' 280 browser_addons: 'افزونه برای مرورگرها'
257 mobile_apps: 'برنامه‌های موبایل' 281 mobile_apps: 'برنامه‌های موبایل'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'افزونهٔ فایرفاکس' 286 firefox: 'افزونهٔ فایرفاکس'
263 chrome: 'افزونهٔ کروم' 287 chrome: 'افزونهٔ کروم'
288 # opera: 'Opera addon'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'از راه F-Droid' 291 via_f_droid: 'از راه F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'از راه Microsoft Store' 294 windows: 'از راه Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'این پیوند را به نوار بوک‌مارک مرورگرتان بکشید:' 296 description: 'این پیوند را به نوار بوک‌مارک مرورگرتان بکشید:'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Quickstart' 326 page_title: 'Quickstart'
@@ -279,6 +331,7 @@ quickstart:
279 paragraph_2: 'ادامه دهید!' 331 paragraph_2: 'ادامه دهید!'
280 configure: 332 configure:
281 title: 'برنامه را تنظیم کنید' 333 title: 'برنامه را تنظیم کنید'
334 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.'
282 language: 'زبان و نمای برنامه را تغییر دهید' 335 language: 'زبان و نمای برنامه را تغییر دهید'
283 rss: 'خوراک آر-اس-اس را فعال کنید' 336 rss: 'خوراک آر-اس-اس را فعال کنید'
284 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید' 337 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید'
@@ -328,6 +381,9 @@ tag:
328 list: 381 list:
329 number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.' 382 number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.'
330 # see_untagged_entries: 'See untagged entries' 383 # see_untagged_entries: 'See untagged entries'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
331 387
332import: 388import:
333 page_title: 'درون‌ریزی' 389 page_title: 'درون‌ریزی'
@@ -361,6 +417,7 @@ import:
361 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
362 worker: 418 worker:
363 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 # 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:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
364 firefox: 421 firefox:
365 page_title: 'درون‌ریزی > Firefox' 422 page_title: 'درون‌ریزی > Firefox'
366 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -373,6 +430,10 @@ import:
373 page_title: 'درون‌ریزی > Instapaper' 430 page_title: 'درون‌ریزی > Instapaper'
374 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
375 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 # page_title: "Import > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
376 437
377developer: 438developer:
378 # page_title: 'Developer' 439 # page_title: 'Developer'
@@ -443,7 +504,6 @@ user:
443 plain_password_label: '????' 504 plain_password_label: '????'
444 email_label: 'نشانی ایمیل' 505 email_label: 'نشانی ایمیل'
445 # enabled_label: 'Enabled' 506 # enabled_label: 'Enabled'
446 # locked_label: 'Locked'
447 # last_login_label: 'Last login' 507 # last_login_label: 'Last login'
448 # twofactor_label: Two factor authentication 508 # twofactor_label: Two factor authentication
449 # save: Save 509 # save: Save
@@ -464,8 +524,10 @@ flashes:
464 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد' 524 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد'
465 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد' 525 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد'
466 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد' 526 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد'
467 user_added: 'کابر "%username%" افزوده شد'
468 rss_token_updated: 'کد آر-اس-اس به‌روز شد' 527 rss_token_updated: 'کد آر-اس-اس به‌روز شد'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
469 entry: 531 entry:
470 notice: 532 notice:
471 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' 533 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود'
@@ -495,3 +557,8 @@ flashes:
495 notice: 557 notice:
496 # client_created: 'New client created.' 558 # client_created: 'New client created.'
497 # client_deleted: 'Client deleted' 559 # client_deleted: 'Client deleted'
560 user:
561 notice:
562 # added: 'User "%username%" added'
563 # updated: 'User "%username%" updated'
564 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index f2c9d8db..6dd449ba 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -46,7 +46,7 @@ footer:
46 social: "Social" 46 social: "Social"
47 powered_by: "propulsé par" 47 powered_by: "propulsé par"
48 about: "À propos" 48 about: "À propos"
49 stats: Depuis le %user_creation% vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! 49 stats: Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour !
50 50
51config: 51config:
52 page_title: "Configuration" 52 page_title: "Configuration"
@@ -70,7 +70,12 @@ config:
70 200_word: "Je lis environ 200 mots par minute" 70 200_word: "Je lis environ 200 mots par minute"
71 300_word: "Je lis environ 300 mots par minute" 71 300_word: "Je lis environ 300 mots par minute"
72 400_word: "Je lis environ 400 mots par minute" 72 400_word: "Je lis environ 400 mots par minute"
73 action_mark_as_read:
74 label: 'Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?'
75 redirect_homepage: "À la page d'accueil"
76 redirect_current_page: 'À la page courante'
73 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données 77 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données
78 android_configuration: Configurez votre application Android
74 help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez." 79 help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez."
75 help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page." 80 help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page."
76 help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article." 81 help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article."
@@ -94,6 +99,18 @@ config:
94 email_label: "Adresse courriel" 99 email_label: "Adresse courriel"
95 twoFactorAuthentication_label: "Double authentification" 100 twoFactorAuthentication_label: "Double authentification"
96 help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." 101 help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email."
102 delete:
103 title: Supprimer mon compte (attention danger !)
104 description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté.
105 confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE)
106 button: 'Supprimer mon compte'
107 reset:
108 title: Réinitialisation (attention danger !)
109 description: En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES !
110 annotations: Supprimer TOUTES les annotations
111 tags: Supprimer TOUS les tags
112 entries: Supprimer TOUS les articles
113 confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE)
97 form_password: 114 form_password:
98 description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." 115 description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères."
99 old_password_label: "Mot de passe actuel" 116 old_password_label: "Mot de passe actuel"
@@ -128,7 +145,7 @@ config:
128 domainName: "Le nom de domaine de l’article" 145 domainName: "Le nom de domaine de l’article"
129 operator_description: 146 operator_description:
130 label: "Opérateur" 147 label: "Opérateur"
131 less_than: "Moins que…..." 148 less_than: "Moins que…"
132 strictly_less_than: "Strictement moins que…" 149 strictly_less_than: "Strictement moins que…"
133 greater_than: "Plus que…" 150 greater_than: "Plus que…"
134 strictly_greater_than: "Strictement plus que…" 151 strictly_greater_than: "Strictement plus que…"
@@ -145,15 +162,16 @@ entry:
145 archived: "Articles lus" 162 archived: "Articles lus"
146 filtered: "Articles filtrés" 163 filtered: "Articles filtrés"
147 filtered_tags: "Articles filtrés par tags :" 164 filtered_tags: "Articles filtrés par tags :"
165 filtered_search: 'Articles filtrés par recherche :'
148 untagged: "Article sans tag" 166 untagged: "Article sans tag"
149 list: 167 list:
150 number_on_the_page: "{0} Il n’y a pas d’articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles." 168 number_on_the_page: "{0} Il n’y a pas d’article.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles."
151 reading_time: "durée de lecture" 169 reading_time: "durée de lecture"
152 reading_time_minutes: "durée de lecture: %readingTime% min" 170 reading_time_minutes: "durée de lecture: %readingTime% min"
153 reading_time_less_one_minute: "durée de lecture: <small class=\"inferieur\">&lt;</small> 1 min" 171 reading_time_less_one_minute: "durée de lecture: &lt; 1 min"
154 number_of_tags: "{1}et un autre tag|]1,Inf[et %count% autres tags" 172 number_of_tags: "{1}et un autre tag|]1,Inf[et %count% autres tags"
155 reading_time_minutes_short: "%readingTime% min" 173 reading_time_minutes_short: "%readingTime% min"
156 reading_time_less_one_minute_short: "<small class=\"inferieur\">&lt;</small> 1 min" 174 reading_time_less_one_minute_short: "&lt; 1 min"
157 original_article: "original" 175 original_article: "original"
158 toogle_as_read: "Marquer comme lu/non lu" 176 toogle_as_read: "Marquer comme lu/non lu"
159 toogle_as_star: "Marquer comme favori" 177 toogle_as_star: "Marquer comme favori"
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: "A une photo" 186 preview_picture_label: "A une photo"
169 preview_picture_help: "Photo" 187 preview_picture_help: "Photo"
170 language_label: "Langue" 188 language_label: "Langue"
189 http_status_label: 'Statut HTTP'
171 reading_time: 190 reading_time:
172 label: "Durée de lecture en minutes" 191 label: "Durée de lecture en minutes"
173 from: "de" 192 from: "de"
@@ -209,6 +228,8 @@ entry:
209 placeholder: "http://website.com" 228 placeholder: "http://website.com"
210 form_new: 229 form_new:
211 url_label: "Adresse" 230 url_label: "Adresse"
231 search:
232 placeholder: "Que recherchez-vous ?"
212 edit: 233 edit:
213 page_title: "Éditer un article" 234 page_title: "Éditer un article"
214 title_label: "Titre" 235 title_label: "Titre"
@@ -251,6 +272,9 @@ about:
251 272
252howto: 273howto:
253 page_title: "Aide" 274 page_title: "Aide"
275 tab_menu:
276 add_link: "Ajouter un lien"
277 shortcuts: "Utiliser les raccourcis"
254 page_description: "Il y a plusieurs façon d’enregistrer un article :" 278 page_description: "Il y a plusieurs façon d’enregistrer un article :"
255 top_menu: 279 top_menu:
256 browser_addons: "Extensions de navigateur" 280 browser_addons: "Extensions de navigateur"
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: "Extension Firefox" 286 firefox: "Extension Firefox"
263 chrome: "Extension Chrome" 287 chrome: "Extension Chrome"
288 opera: "Extension Opera"
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: "via F-Droid" 291 via_f_droid: "via F-Droid"
@@ -269,6 +294,33 @@ howto:
269 windows: "sur Microsoft Store" 294 windows: "sur Microsoft Store"
270 bookmarklet: 295 bookmarklet:
271 description: "Glissez et déposez ce lien dans votre barre de favoris :" 296 description: "Glissez et déposez ce lien dans votre barre de favoris :"
297 shortcuts:
298 page_description: Voici les raccourcis disponibles dans wallabag.
299 shortcut: Raccourci
300 action: Action
301 all_pages_title: Raccourcis disponibles dans toutes les pages
302 go_unread: Afficher les articles non lus
303 go_starred: Afficher les articles favoris
304 go_archive: Afficher les articles lus
305 go_all: Afficher tous les articles
306 go_tags: Afficher les tags
307 go_config: Aller à la configuration
308 go_import: Aller aux imports
309 go_developers: Aller à la section Développeurs
310 go_howto: Afficher l'aide (cette page !)
311 go_logout: Se déconnecter
312 list_title: Raccourcis disponibles dans les pages de liste
313 search: Afficher le formulaire de recherche
314 article_title: Raccourcis disponibles quand on affiche un article
315 open_original: Ouvrir l'URL originale de l'article
316 toggle_favorite: Changer le statut Favori de l'article
317 toggle_archive: Changer le status Lu de l'article
318 delete: Supprimer l'article
319 material_title: Raccourcis disponibles avec le thème Material uniquement
320 add_link: Ajouter un nouvel article
321 hide_form: Masquer le formulaire courant (recherche ou nouvel article)
322 arrows_navigation: Naviguer à travers les articles
323 open_article: Afficher l'article sélectionné
272 324
273quickstart: 325quickstart:
274 page_title: "Pour bien débuter" 326 page_title: "Pour bien débuter"
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: "{0} Il n’y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags." 382 number_on_the_page: "{0} Il n’y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags."
331 see_untagged_entries: "Voir les articles sans tag" 383 see_untagged_entries: "Voir les articles sans tag"
384 new:
385 add: 'Ajouter'
386 placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.'
332 387
333import: 388import:
334 page_title: "Importer" 389 page_title: "Importer"
@@ -362,9 +417,10 @@ import:
362 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." 417 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer."
363 worker: 418 worker:
364 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" 419 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :"
420 download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l'import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons <strong>vivement</strong> d'activer les imports asynchrones."
365 firefox: 421 firefox:
366 page_title: "Import > Firefox" 422 page_title: "Import > Firefox"
367 description: "Cet outil va vous permettre d’importer tous vos marques-pages de Firefox. Ouvrez le panneau des marques-pages (Ctrl+Maj+O), puis dans « Importation et sauvegarde », choisissez « Sauvegarde... ». Vous allez récupérer un fichier .json. </p>" 423 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>"
368 how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour l’importer. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne." 424 how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour l’importer. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne."
369 chrome: 425 chrome:
370 page_title: "Import > Chrome" 426 page_title: "Import > Chrome"
@@ -374,6 +430,10 @@ import:
374 page_title: "Import > Instapaper" 430 page_title: "Import > Instapaper"
375 description: "Sur la page des paramètres (https://www.instapaper.com/user), cliquez sur « Download .CSV file » dans la section « Export ». Un fichier CSV sera téléchargé (« instapaper-export.csv »)." 431 description: "Sur la page des paramètres (https://www.instapaper.com/user), cliquez sur « Download .CSV file » dans la section « Export ». Un fichier CSV sera téléchargé (« instapaper-export.csv »)."
376 how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l’importer." 432 how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l’importer."
433 pinboard:
434 page_title: "Import > Pinboard"
435 description: "Sur la page « Backup » (https://pinboard.in/settings/backup), cliquez sur « JSON » dans la section « Bookmarks ». Un fichier json (sans extension) sera téléchargé (« pinboard_export »)."
436 how_to: "Choisissez le fichier de votre export Pinboard et cliquez sur le bouton ci-dessous pour l’importer."
377 437
378developer: 438developer:
379 page_title: "Développeur" 439 page_title: "Développeur"
@@ -444,7 +504,6 @@ user:
444 plain_password_label: "Mot de passe en clair" 504 plain_password_label: "Mot de passe en clair"
445 email_label: "Adresse courriel" 505 email_label: "Adresse courriel"
446 enabled_label: "Activé" 506 enabled_label: "Activé"
447 locked_label: "Bloqué"
448 last_login_label: "Dernière connexion" 507 last_login_label: "Dernière connexion"
449 twofactor_label: "Double authentification" 508 twofactor_label: "Double authentification"
450 save: "Sauvegarder" 509 save: "Sauvegarder"
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: "La configuration des flux RSS a bien été mise à jour" 524 rss_updated: "La configuration des flux RSS a bien été mise à jour"
466 tagging_rules_updated: "Règles mises à jour" 525 tagging_rules_updated: "Règles mises à jour"
467 tagging_rules_deleted: "Règle supprimée" 526 tagging_rules_deleted: "Règle supprimée"
468 user_added: "Utilisateur \"%username%\" ajouté"
469 rss_token_updated: "Jeton RSS mis à jour" 527 rss_token_updated: "Jeton RSS mis à jour"
528 annotations_reset: Annotations supprimées
529 tags_reset: Tags supprimés
530 entries_reset: Articles supprimés
470 entry: 531 entry:
471 notice: 532 notice:
472 entry_already_saved: "Article déjà sauvegardé le %date%" 533 entry_already_saved: "Article déjà sauvegardé le %date%"
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 client_created: "Nouveau client %name% créé" 558 client_created: "Nouveau client %name% créé"
498 client_deleted: "Client %name% supprimé" 559 client_deleted: "Client %name% supprimé"
560 user:
561 notice:
562 added: 'Utilisateur "%username%" ajouté'
563 updated: 'Utilisateur "%username%" mis à jour'
564 deleted: 'Utilisateur "%username%" supprimé'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
index b7d066ab..4298d1f7 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Leggo ~200 parole al minuto' 70 200_word: 'Leggo ~200 parole al minuto'
71 300_word: 'Leggo ~300 parole al minuto' 71 300_word: 'Leggo ~300 parole al minuto'
72 400_word: 'Leggo ~400 parole al minuto' 72 400_word: 'Leggo ~400 parole al minuto'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti 77 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-mail' 99 email_label: 'E-mail'
95 twoFactorAuthentication_label: 'Two factor authentication' 100 twoFactorAuthentication_label: 'Two factor authentication'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Password corrente' 116 old_password_label: 'Password corrente'
@@ -145,15 +162,16 @@ entry:
145 archived: 'Contenuti archiviati' 162 archived: 'Contenuti archiviati'
146 filtered: 'Contenuti filtrati' 163 filtered: 'Contenuti filtrati'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti." 168 number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti."
151 reading_time: 'tempo di lettura stimato' 169 reading_time: 'tempo di lettura stimato'
152 reading_time_minutes: 'tempo di lettura stimato: %readingTime% min' 170 reading_time_minutes: 'tempo di lettura stimato: %readingTime% min'
153 reading_time_less_one_minute: 'tempo di lettura stimato: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'tempo di lettura stimato: &lt; 1 min'
154 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'originale' 175 original_article: 'originale'
158 toogle_as_read: 'Segna come da leggere' 176 toogle_as_read: 'Segna come da leggere'
159 toogle_as_star: 'Segna come non preferito' 177 toogle_as_star: 'Segna come non preferito'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: "Ha un'immagine di anteprima" 186 preview_picture_label: "Ha un'immagine di anteprima"
169 preview_picture_help: 'Immagine di anteprima' 187 preview_picture_help: 'Immagine di anteprima'
170 language_label: 'Lingua' 188 language_label: 'Lingua'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Tempo di lettura in minuti' 191 label: 'Tempo di lettura in minuti'
173 from: 'da' 192 from: 'da'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Modifica voce' 234 page_title: 'Modifica voce'
214 title_label: 'Titolo' 235 title_label: 'Titolo'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'How to' 274 page_title: 'How to'
254 page_description: 'Ci sono diversi modi per salvare un contenuto:' 275 page_description: 'Ci sono diversi modi per salvare un contenuto:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'tramite addons del Browser' 280 browser_addons: 'tramite addons del Browser'
257 mobile_apps: 'tramite app Mobile' 281 mobile_apps: 'tramite app Mobile'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Add-On di Firefox' 286 firefox: 'Add-On di Firefox'
263 chrome: 'Estensione di Chrome' 287 chrome: 'Estensione di Chrome'
288 opera: 'Estensione di Opera'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'sullo store di Microsoft' 294 windows: 'sullo store di Microsoft'
270 bookmarklet: 295 bookmarklet:
271 description: 'Trascinando e rilasciando questo link sulla barra dei bookmark del tuo browser:' 296 description: 'Trascinando e rilasciando questo link sulla barra dei bookmark del tuo browser:'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Introduzione' 326 page_title: 'Introduzione'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: "{0} Non ci sono tag.|{1} C'è un tag.|]1,Inf[ ci sono %count% tag." 382 number_on_the_page: "{0} Non ci sono tag.|{1} C'è un tag.|]1,Inf[ ci sono %count% tag."
331 # see_untagged_entries: 'See untagged entries' 383 # see_untagged_entries: 'See untagged entries'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 page_title: 'Importa' 389 page_title: 'Importa'
@@ -362,6 +417,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 418 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 # 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:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 421 firefox:
366 page_title: 'Importa da > Firefox' 422 page_title: 'Importa da > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Importa da > Instapaper' 430 page_title: 'Importa da > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 page_title: "Importa da > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 page_title: 'Sviluppatori' 439 page_title: 'Sviluppatori'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'E-mail' 505 email_label: 'E-mail'
446 # enabled_label: 'Enabled' 506 # enabled_label: 'Enabled'
447 # locked_label: 'Locked'
448 # last_login_label: 'Last login' 507 # last_login_label: 'Last login'
449 # twofactor_label: Two factor authentication 508 # twofactor_label: Two factor authentication
450 # save: Save 509 # save: Save
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'Informazioni RSS aggiornate' 524 rss_updated: 'Informazioni RSS aggiornate'
466 tagging_rules_updated: 'Regole di tagging aggiornate' 525 tagging_rules_updated: 'Regole di tagging aggiornate'
467 tagging_rules_deleted: 'Regola di tagging aggiornate' 526 tagging_rules_deleted: 'Regola di tagging aggiornate'
468 user_added: 'Utente "%username%" aggiunto'
469 rss_token_updated: 'RSS token aggiornato' 527 rss_token_updated: 'RSS token aggiornato'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
470 entry: 531 entry:
471 notice: 532 notice:
472 entry_already_saved: 'Contenuto già salvato in data %date%' 533 entry_already_saved: 'Contenuto già salvato in data %date%'
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 client_created: 'Nuovo client creato.' 558 client_created: 'Nuovo client creato.'
498 client_deleted: 'Client eliminato' 559 client_deleted: 'Client eliminato'
560 user:
561 notice:
562 # added: 'User "%username%" added'
563 # updated: 'User "%username%" updated'
564 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
index 16ef1e5d..85310cd7 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
@@ -25,13 +25,13 @@ menu:
25 internal_settings: 'Configuracion interna' 25 internal_settings: 'Configuracion interna'
26 import: 'Importar' 26 import: 'Importar'
27 howto: 'Ajuda' 27 howto: 'Ajuda'
28 developer: 'Desvolopador' 28 developer: 'Desvolopaire'
29 logout: 'Desconnexion' 29 logout: 'Desconnexion'
30 about: 'A prepaus' 30 about: 'A prepaus'
31 search: 'Cercar' 31 search: 'Cercar'
32 save_link: 'Enregistrar un novèl article' 32 save_link: 'Enregistrar un novèl article'
33 back_to_unread: 'Tornar als articles pas legits' 33 back_to_unread: 'Tornar als articles pas legits'
34 # users_management: 'Users management' 34 users_management: 'Gestion dels utilizaires'
35 top: 35 top:
36 add_new_entry: 'Enregistrar un novèl article' 36 add_new_entry: 'Enregistrar un novèl article'
37 search: 'Cercar' 37 search: 'Cercar'
@@ -46,7 +46,7 @@ footer:
46 social: 'Social' 46 social: 'Social'
47 powered_by: 'propulsat per' 47 powered_by: 'propulsat per'
48 about: 'A prepaus' 48 about: 'A prepaus'
49 # stats: Since %user_creation% you read %nb_archives% articles. That is about %per_day% a day! 49 stats: "Dempuèi %user_creation% avètz legit %nb_archives% articles. Es a l'entorn de %per_day% per jorn !"
50 50
51config: 51config:
52 page_title: 'Configuracion' 52 page_title: 'Configuracion'
@@ -70,7 +70,12 @@ config:
70 200_word: "Legissi a l'entorn de 200 mots per minuta" 70 200_word: "Legissi a l'entorn de 200 mots per minuta"
71 300_word: "Legissi a l'entorn de 300 mots per minuta" 71 300_word: "Legissi a l'entorn de 300 mots per minuta"
72 400_word: "Legissi a l'entorn de 400 mots per minuta" 72 400_word: "Legissi a l'entorn de 400 mots per minuta"
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas 77 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas
78 android_configuration: Configuratz vòstra aplicacion Android
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Adreça de corrièl' 99 email_label: 'Adreça de corrièl'
95 twoFactorAuthentication_label: 'Dobla autentificacion' 100 twoFactorAuthentication_label: 'Dobla autentificacion'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Suprimir mon compte (Mèfi zòna perilhosa)
104 description: Se confirmatz la supression de vòstre compte, TOTES vòstres articles, TOTAS vòstras etiquetas, TOTAS vòstras anotacions e vòstre compte seràn suprimits per totjorn. E aquò es IRREVERSIBLE. Puèi seretz desconnectat.
105 confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE)
106 button: Suprimir mon compte
107 reset:
108 title: Zòna de reïnicializacion (Mèfi zòna perilhosa)
109 description: En clicant sul boton çai-jos auretz la possibilitat de levar qualques informacions de vòstre compte. Mèfi que totas aquelas accions son IRREVERSIBLAS.
110 annotations: Levar TOTAS las anotacions
111 tags: Levar TOTAS las etiquetas
112 entries: Levar TOTES los articles
113 confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Senhal actual' 116 old_password_label: 'Senhal actual'
@@ -103,7 +120,7 @@ config:
103 if_label: 'se' 120 if_label: 'se'
104 then_tag_as_label: 'alara atribuir las etiquetas' 121 then_tag_as_label: 'alara atribuir las etiquetas'
105 delete_rule_label: 'suprimir' 122 delete_rule_label: 'suprimir'
106 # edit_rule_label: 'edit' 123 edit_rule_label: 'modificar'
107 rule_label: 'Règla' 124 rule_label: 'Règla'
108 tags_label: 'Etiquetas' 125 tags_label: 'Etiquetas'
109 faq: 126 faq:
@@ -145,15 +162,16 @@ entry:
145 archived: 'Articles legits' 162 archived: 'Articles legits'
146 filtered: 'Articles filtrats' 163 filtered: 'Articles filtrats'
147 filtered_tags: 'Filtats per etiquetas:' 164 filtered_tags: 'Filtats per etiquetas:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Articles sens etiqueta' 166 untagged: 'Articles sens etiqueta'
149 list: 167 list:
150 number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles." 168 number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles."
151 reading_time: 'durada de lectura' 169 reading_time: 'durada de lectura'
152 reading_time_minutes: 'durada de lectura : %readingTime% min' 170 reading_time_minutes: 'durada de lectura : %readingTime% min'
153 reading_time_less_one_minute: 'durada de lectura : <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'durada de lectura : &lt; 1 min'
154 number_of_tags: '{1}e una autra etiqueta|]1,Inf[e %count% autras etiquetas' 172 number_of_tags: '{1}e una autra etiqueta|]1,Inf[e %count% autras etiquetas'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'Marcar coma legit/pas legit' 176 toogle_as_read: 'Marcar coma legit/pas legit'
159 toogle_as_star: 'Marcar coma favorit' 177 toogle_as_star: 'Marcar coma favorit'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'A una fotò' 186 preview_picture_label: 'A una fotò'
169 preview_picture_help: 'Fotò' 187 preview_picture_help: 'Fotò'
170 language_label: 'Lenga' 188 language_label: 'Lenga'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Durada de lectura en minutas' 191 label: 'Durada de lectura en minutas'
173 from: 'de' 192 from: 'de'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Modificar un article' 234 page_title: 'Modificar un article'
214 title_label: 'Títol' 235 title_label: 'Títol'
@@ -216,7 +237,7 @@ entry:
216 is_public_label: 'Public' 237 is_public_label: 'Public'
217 save_label: 'Enregistrar' 238 save_label: 'Enregistrar'
218 public: 239 public:
219 # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" 240 shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>"
220 241
221about: 242about:
222 page_title: 'A prepaus' 243 page_title: 'A prepaus'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'Ajuda' 274 page_title: 'Ajuda'
254 page_description: "I a mai d'un biais d'enregistrar un article :" 275 page_description: "I a mai d'un biais d'enregistrar un article :"
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'Extensions de navigator' 280 browser_addons: 'Extensions de navigator'
257 mobile_apps: 'Aplicacions mobil' 281 mobile_apps: 'Aplicacions mobil'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Extension Firefox' 286 firefox: 'Extension Firefox'
263 chrome: 'Extension Chrome' 287 chrome: 'Extension Chrome'
288 opera: 'Extension Opera'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,17 +294,44 @@ howto:
269 windows: 'sus Microsoft Store' 294 windows: 'sus Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Lisatz-depausatz aqueste ligam dins vòstra barra de favorits :' 296 description: 'Lisatz-depausatz aqueste ligam dins vòstra barra de favorits :'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Per ben començar' 326 page_title: 'Per ben començar'
275 # more: 'More…' 327 more: 'Mai…'
276 intro: 328 intro:
277 title: 'Benvenguda sus wallabag !' 329 title: 'Benvenguda sus wallabag !'
278 paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina." 330 paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina."
279 paragraph_2: 'Seguètz-nos ' 331 paragraph_2: 'Seguètz-nos '
280 configure: 332 configure:
281 title: "Configuratz l'aplicacio" 333 title: "Configuratz l'aplicacion"
282 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.' 334 description: "Per fin d'aver una aplicacion que vos va ben, anatz veire la configuracion de wallabag."
283 language: "Cambiatz la lenga e l'estil de l'aplicacion" 335 language: "Cambiatz la lenga e l'estil de l'aplicacion"
284 rss: 'Activatz los fluxes RSS' 336 rss: 'Activatz los fluxes RSS'
285 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' 337 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles'
@@ -293,7 +345,7 @@ quickstart:
293 import: 'Configurar los impòrt' 345 import: 'Configurar los impòrt'
294 first_steps: 346 first_steps:
295 title: 'Primièrs passes' 347 title: 'Primièrs passes'
296 # description: "Now wallabag is well configured, it's time to archive the web. You can click on the top right sign + to add a link." 348 description: "Ara wallabag es ben configurat, es lo moment d'archivar lo web. Podètz clicar sul signe + a man drecha amont per ajustar un ligam."
297 new_article: 'Ajustatz vòstre primièr article' 349 new_article: 'Ajustatz vòstre primièr article'
298 unread_articles: 'E racaptatz-lo !' 350 unread_articles: 'E racaptatz-lo !'
299 migrate: 351 migrate:
@@ -305,14 +357,14 @@ quickstart:
305 readability: 'Migrar dempuèi Readability' 357 readability: 'Migrar dempuèi Readability'
306 instapaper: 'Migrar dempuèi Instapaper' 358 instapaper: 'Migrar dempuèi Instapaper'
307 developer: 359 developer:
308 title: 'Pels desvolopadors' 360 title: 'Pels desvolopaires'
309 # description: 'We also thought to the developers: Docker, API, translations, etc.' 361 description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.'
310 create_application: 'Crear vòstra aplicacion tèrça' 362 create_application: 'Crear vòstra aplicacion tèrça'
311 # use_docker: 'Use Docker to install wallabag' 363 use_docker: 'Utilizar Docker per installar wallabag'
312 docs: 364 docs:
313 title: 'Documentacion complèta' 365 title: 'Documentacion complèta'
314 # description: "There are so much features in wallabag. Don't hesitate to read the manual to know them and to learn how to use them." 366 description: "I a un fum de fonccionalitats dins wallabag. Esitetz pas a legir lo manual per las conéisser e aprendre a las utilizar."
315 annotate: 'Anotatar vòstre article' 367 annotate: 'Anotar vòstre article'
316 export: 'Convertissètz vòstres articles en ePub o en PDF' 368 export: 'Convertissètz vòstres articles en ePub o en PDF'
317 search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" 369 search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa"
318 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" 370 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?"
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas." 382 number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas."
331 see_untagged_entries: "Afichar las entradas sens pas cap d'etiquetas" 383 see_untagged_entries: "Afichar las entradas sens pas cap d'etiquetas"
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 page_title: 'Importar' 389 page_title: 'Importar'
@@ -362,6 +417,7 @@ import:
362 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar." 417 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar."
363 worker: 418 worker:
364 enabled: "L'importacion se fa de manièra asincròna. Un còp l'importacion lançada, una aisina externa s'ocuparà dels messatges un per un. Lo servici actual es : " 419 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 : "
420 download_images_warning: "Avètz activat lo telecargament de los imatges de vòstres articles. Combinat amb l'importacion classica, aquò pòt tardar un long moment (o benlèu fracassar). <strong>Recomandem fòrtament</strong> l'activacion de l'importacion asincròna per evitar las errors."
365 firefox: 421 firefox:
366 page_title: 'Importar > Firefox' 422 page_title: 'Importar > Firefox'
367 description: "Aquesta aisina importarà totas vòstres favorits de Firefox. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 description: "Aquesta aisina importarà totas vòstres favorits de Firefox. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Importar > Instapaper' 430 page_title: 'Importar > Instapaper'
375 description: "Aquesta aisina importarà totas vòstres articles d'Instapaper. Sus la pagina de paramètres (https://www.instapaper.com/user), clicatz sus \"Download .CSV file\" dins la seccion \"Export\". Un fichièr CSV serà telecargat (aital \"instapaper-export.csv\")." 431 description: "Aquesta aisina importarà totas vòstres articles d'Instapaper. Sus la pagina de paramètres (https://www.instapaper.com/user), clicatz sus \"Download .CSV file\" dins la seccion \"Export\". Un fichièr CSV serà telecargat (aital \"instapaper-export.csv\")."
376 how_to: "Mercés de causir vòstre fichièr Instapaper e de clicar sul boton dejós per lo telecargar e l'importar" 432 how_to: "Mercés de causir vòstre fichièr Instapaper e de clicar sul boton dejós per lo telecargar e l'importar"
433 pinboard:
434 # page_title: "Import > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 page_title: 'Desvolopaire' 439 page_title: 'Desvolopaire'
@@ -397,7 +457,7 @@ developer:
397 warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." 457 warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag."
398 action: 'Suprimir aqueste client' 458 action: 'Suprimir aqueste client'
399 client: 459 client:
400 page_title: 'Desvlopador > Novèl client' 460 page_title: 'Desvolopaire > Novèl client'
401 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." 461 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion."
402 form: 462 form:
403 name_label: "Nom del client" 463 name_label: "Nom del client"
@@ -405,7 +465,7 @@ developer:
405 save_label: 'Crear un novèl client' 465 save_label: 'Crear un novèl client'
406 action_back: 'Retorn' 466 action_back: 'Retorn'
407 client_parameter: 467 client_parameter:
408 page_title: 'Desvolopador > Los paramètres de vòstre client' 468 page_title: 'Desvolopaire > Los paramètres de vòstre client'
409 page_description: 'Vaquí los paramètres de vòstre client' 469 page_description: 'Vaquí los paramètres de vòstre client'
410 field_name: 'Nom del client' 470 field_name: 'Nom del client'
411 field_id: 'ID Client' 471 field_id: 'ID Client'
@@ -413,7 +473,7 @@ developer:
413 back: 'Retour' 473 back: 'Retour'
414 read_howto: 'Legir "cossí crear ma primièra aplicacion"' 474 read_howto: 'Legir "cossí crear ma primièra aplicacion"'
415 howto: 475 howto:
416 page_title: 'Desvolopador > Cossí crear ma primièra aplicacion' 476 page_title: 'Desvolopaire > Cossí crear ma primièra aplicacion'
417 description: 477 description:
418 paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar." 478 paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar."
419 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." 479 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar."
@@ -426,34 +486,33 @@ developer:
426 back: 'Retorn' 486 back: 'Retorn'
427 487
428user: 488user:
429 # page_title: Users management 489 page_title: 'Gestion dels utilizaires'
430 # new_user: Create a new user 490 new_user: 'Crear un novèl utilizaire'
431 # edit_user: Edit an existing user 491 edit_user: 'Modificar un utilizaire existent'
432 # description: "Here you can manage all users (create, edit and delete)" 492 description: "Aquí podètz gerir totes los utilizaires (crear, modificar e suprimir)"
433 # list: 493 list:
434 # actions: Actions 494 actions: 'Accions'
435 # edit_action: Edit 495 edit_action: 'Modificar'
436 # yes: Yes 496 yes: 'Òc'
437 # no: No 497 no: 'Non'
438 # create_new_one: Create a new user 498 create_new_one: 'Crear un novèl utilizaire'
439 form: 499 form:
440 username_label: "Nom d'utilizaire" 500 username_label: "Nom d'utilizaire"
441 # name_label: 'Name' 501 name_label: 'Nom'
442 password_label: 'Senhal' 502 password_label: 'Senhal'
443 repeat_new_password_label: 'Confirmatz vòstre novèl senhal' 503 repeat_new_password_label: 'Confirmatz vòstre novèl senhal'
444 plain_password_label: 'Senhal en clar' 504 plain_password_label: 'Senhal en clar'
445 email_label: 'Adreça de corrièl' 505 email_label: 'Adreça de corrièl'
446 # enabled_label: 'Enabled' 506 enabled_label: 'Actiu'
447 # locked_label: 'Locked' 507 last_login_label: 'Darrièra connexion'
448 # last_login_label: 'Last login' 508 twofactor_label: 'Autentificacion doble-factor'
449 # twofactor_label: Two factor authentication 509 save: 'Enregistrar'
450 # save: Save 510 delete: 'Suprimir'
451 # delete: Delete 511 delete_confirm: 'Sètz segur ?'
452 # delete_confirm: Are you sure? 512 back_to_list: 'Tornar a la lista'
453 # back_to_list: Back to list
454 513
455error: 514error:
456 # page_title: An error occurred 515 page_title: Una error s'es produsida
457 516
458flashes: 517flashes:
459 config: 518 config:
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' 524 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn'
466 tagging_rules_updated: 'Règlas misa a jorn' 525 tagging_rules_updated: 'Règlas misa a jorn'
467 tagging_rules_deleted: 'Règla suprimida' 526 tagging_rules_deleted: 'Règla suprimida'
468 user_added: 'Utilizaire "%username%" apondut'
469 rss_token_updated: 'Geton RSS mes a jorn' 527 rss_token_updated: 'Geton RSS mes a jorn'
528 annotations_reset: Anotacions levadas
529 tags_reset: Etiquetas levadas
530 entries_reset: Articles levats
470 entry: 531 entry:
471 notice: 532 notice:
472 entry_already_saved: 'Article ja salvargardat lo %date%' 533 entry_already_saved: 'Article ja salvargardat lo %date%'
@@ -477,12 +538,12 @@ flashes:
477 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat" 538 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat"
478 entry_archived: 'Article marcat coma legit' 539 entry_archived: 'Article marcat coma legit'
479 entry_unarchived: 'Article marcat coma pas legit' 540 entry_unarchived: 'Article marcat coma pas legit'
480 entry_starred: 'Article apondut dins los favorits' 541 entry_starred: 'Article ajustat dins los favorits'
481 entry_unstarred: 'Article quitat dels favorits' 542 entry_unstarred: 'Article quitat dels favorits'
482 entry_deleted: 'Article suprimit' 543 entry_deleted: 'Article suprimit'
483 tag: 544 tag:
484 notice: 545 notice:
485 tag_added: 'Etiqueta aponduda' 546 tag_added: 'Etiqueta ajustada'
486 import: 547 import:
487 notice: 548 notice:
488 failed: "L'importacion a fracassat, mercés de tornar ensajar" 549 failed: "L'importacion a fracassat, mercés de tornar ensajar"
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 client_created: 'Novèl client creat' 558 client_created: 'Novèl client creat'
498 client_deleted: 'Client suprimit' 559 client_deleted: 'Client suprimit'
560 user:
561 notice:
562 added: 'Utilizaire "%username%" ajustat'
563 updated: 'Utilizaire "%username%" mes a jorn'
564 deleted: 'Utilizaire "%username%" suprimit'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index 73250cc0..35120edc 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -61,7 +61,7 @@ config:
61 save: 'Zapisz' 61 save: 'Zapisz'
62 form_settings: 62 form_settings:
63 theme_label: 'Temat' 63 theme_label: 'Temat'
64 items_per_page_label: 'Ilość elementóœ na stronie' 64 items_per_page_label: 'Ilość elementów na stronie'
65 language_label: 'Język' 65 language_label: 'Język'
66 reading_speed: 66 reading_speed:
67 label: 'Prędkość czytania' 67 label: 'Prędkość czytania'
@@ -70,12 +70,17 @@ config:
70 200_word: 'Czytam ~200 słów na minutę' 70 200_word: 'Czytam ~200 słów na minutę'
71 300_word: 'Czytam ~300 słów na minutę' 71 300_word: 'Czytam ~300 słów na minutę'
72 400_word: 'Czytam ~400 słów na minutę' 72 400_word: 'Czytam ~400 słów na minutę'
73 action_mark_as_read:
74 label: 'Gdzie zostaniesz przekierowany po oznaczeniu artukuły jako przeczytanego'
75 redirect_homepage: 'do strony głównej'
76 redirect_current_page: 'do bieżącej strony'
73 pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości' 77 pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości'
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 78 android_configuration: Skonfiguruj swoją androidową aplikację
75 # help_items_per_page: "You can change the number of articles displayed on each page." 79 help_theme: "Dopasuj wallabag do swoich potrzeb. Tutaj możesz wybrać preferowany przez ciebie motyw."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 80 help_items_per_page: "Możesz zmienić ilość artykułów wyświetlanych na każdej stronie."
77 # help_language: "You can change the language of wallabag interface." 81 help_reading_speed: "wallabag oblicza czas czytania każdego artykułu. Dzięki tej liście możesz określić swoje tempo. Wallabag przeliczy ponownie czas potrzebny, na przeczytanie każdego z artykułów."
78 # help_pocket_consumer_key: "Required for Pocket import. You can create it in your Pocket account." 82 help_language: "Możesz zmienić język interfejsu wallabag."
83 help_pocket_consumer_key: "Wymagane dla importu z Pocket. Możesz go stworzyć na swoim koncie Pocket."
79 form_rss: 84 form_rss:
80 description: 'Kanały RSS prowadzone przez wallabag pozwalają Ci na czytanie twoich zapisanych artykułów w twoium ulubionym czytniku RSS. Musisz najpierw wynegenerować tokena.‌' 85 description: 'Kanały RSS prowadzone przez wallabag pozwalają Ci na czytanie twoich zapisanych artykułów w twoium ulubionym czytniku RSS. Musisz najpierw wynegenerować tokena.‌'
81 token_label: 'Token RSS' 86 token_label: 'Token RSS'
@@ -93,9 +98,21 @@ config:
93 name_label: 'Nazwa' 98 name_label: 'Nazwa'
94 email_label: 'Adres email' 99 email_label: 'Adres email'
95 twoFactorAuthentication_label: 'Autoryzacja dwuetapowa' 100 twoFactorAuthentication_label: 'Autoryzacja dwuetapowa'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 help_twoFactorAuthentication: "Jeżeli włączysz autoryzację dwuetapową. Za każdym razem, kiedy będziesz chciał się zalogować, dostaniesz kod na swój e-mail."
102 delete:
103 title: Usuń moje konto (niebezpieczna strefa !)
104 description: Jeżeli usuniesz swoje konto, wszystkie twoje artykuły, tagi, adnotacje, oraz konto zostaną trwale usunięte (operacja jest NIEODWRACALNA). Następnie zostaniesz wylogowany.
105 confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć)
106 button: Usuń moje konto
107 reset:
108 title: Reset (niebezpieczna strefa)
109 description: Poniższe przyciski pozwalają usunąć pewne informacje z twojego konta. Uważaj te operacje są NIEODWRACALNE.
110 annotations: Usuń WSZYSTKIE adnotacje
111 tags: Usuń WSZYSTKIE tagi
112 entries: usuń WSZYTSTKIE wpisy
113 confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 description: "Tutaj możesz zmienić swoje hasło. Twoje nowe hasło powinno mieć conajmniej 8 znaków."
99 old_password_label: 'Stare hasło' 116 old_password_label: 'Stare hasło'
100 new_password_label: 'Nowe hasło' 117 new_password_label: 'Nowe hasło'
101 repeat_new_password_label: 'Powtórz nowe hasło' 118 repeat_new_password_label: 'Powtórz nowe hasło'
@@ -145,15 +162,16 @@ entry:
145 archived: 'Zarchiwizowane wpisy' 162 archived: 'Zarchiwizowane wpisy'
146 filtered: 'Odfiltrowane wpisy' 163 filtered: 'Odfiltrowane wpisy'
147 filtered_tags: 'Filtrowane po tagach:' 164 filtered_tags: 'Filtrowane po tagach:'
165 filtered_search: 'Filtrowanie po wyszukiwaniu:'
148 untagged: 'Odtaguj wpisy' 166 untagged: 'Odtaguj wpisy'
149 list: 167 list:
150 number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.' 168 number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.'
151 reading_time: 'szacunkowy czas czytania' 169 reading_time: 'szacunkowy czas czytania'
152 reading_time_minutes: 'szacunkowy czas czytania: %readingTime% min' 170 reading_time_minutes: 'szacunkowy czas czytania: %readingTime% min'
153 reading_time_less_one_minute: 'szacunkowy czas czytania: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'szacunkowy czas czytania: &lt; 1 min'
154 number_of_tags: '{1} i inny tag|]1,Inf[i %count% innych tagów' 172 number_of_tags: '{1} i inny tag|]1,Inf[i %count% innych tagów'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'oryginał' 175 original_article: 'oryginał'
158 toogle_as_read: 'Oznacz jako przeczytane' 176 toogle_as_read: 'Oznacz jako przeczytane'
159 toogle_as_star: 'Oznacz jako ulubione' 177 toogle_as_star: 'Oznacz jako ulubione'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Posiada podgląd obrazu' 186 preview_picture_label: 'Posiada podgląd obrazu'
169 preview_picture_help: 'Podgląd obrazu' 187 preview_picture_help: 'Podgląd obrazu'
170 language_label: 'Język' 188 language_label: 'Język'
189 http_status_label: 'Status HTTP'
171 reading_time: 190 reading_time:
172 label: 'Czas czytania w minutach' 191 label: 'Czas czytania w minutach'
173 from: 'od' 192 from: 'od'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 placeholder: 'Czego szukasz?'
212 edit: 233 edit:
213 page_title: 'Edytuj wpis' 234 page_title: 'Edytuj wpis'
214 title_label: 'Tytuł' 235 title_label: 'Tytuł'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'Howto' 274 page_title: 'Howto'
254 page_description: 'Sposoby zapisania artykułu:' 275 page_description: 'Sposoby zapisania artykułu:'
276 tab_menu:
277 add_link: "Dodaj link"
278 shortcuts: "Użyj skrótów"
255 top_menu: 279 top_menu:
256 browser_addons: 'Wtyczki dla przeglądarki' 280 browser_addons: 'Wtyczki dla przeglądarki'
257 mobile_apps: 'Aplikacje mobilne' 281 mobile_apps: 'Aplikacje mobilne'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Standardowe rozszerzenie dla Firefox' 286 firefox: 'Standardowe rozszerzenie dla Firefox'
263 chrome: 'Rozszerzenie dla Chrome' 287 chrome: 'Rozszerzenie dla Chrome'
288 opera: 'Rozszerzenie dla Opera'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'w F-Droid' 291 via_f_droid: 'w F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'w Microsoft Store' 294 windows: 'w Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Przeciągnij i upuść ten link na swój pasek zakładek' 296 description: 'Przeciągnij i upuść ten link na swój pasek zakładek'
297 shortcuts:
298 page_description: Tutaj znajdują się skróty dostępne w wallabag.
299 shortcut: Skrót
300 action: Akcja
301 all_pages_title: Skróty dostępne na wszystkich stronach
302 go_unread: Idź do nieprzeczytanych
303 go_starred: Idź do oznaczonych gwiazdką
304 go_archive: Idź do archiwum
305 go_all: Idź do wszystkich wpisów
306 go_tags: Idź do tagów
307 go_config: Idź do konfiguracji
308 go_import: Idź do importu
309 go_developers: Idź do deweloperów
310 go_howto: Idź do howto (tej strony!)
311 go_logout: Wyloguj
312 list_title: Skróty dostępne w spisie stron
313 search: Pokaż formularz wyszukiwania
314 article_title: Skróty dostępne w widoku artykułu
315 open_original: Otwórz oryginalny adres URL wpisu
316 toggle_favorite: Oznacz wpis gwiazdką
317 toggle_archive: Oznacz wpis jako przeczytany
318 delete: Usuń wpis
319 material_title: Skróty dostępne wyłącznie w motywie Material
320 add_link: Dodaj nowy link
321 hide_form: Ukryj obecny formularz (wyszukiwania lub nowego linku)
322 arrows_navigation: Nawiguj pomiędzy artykułami
323 open_article: Wyświetl zaznaczony wpis
272 324
273quickstart: 325quickstart:
274 page_title: 'Szybki start' 326 page_title: 'Szybki start'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: '{0} Nie ma tagów.|{1} Jest jeden tag.|]1,Inf[ Są %count% tagi.' 382 number_on_the_page: '{0} Nie ma tagów.|{1} Jest jeden tag.|]1,Inf[ Są %count% tagi.'
331 see_untagged_entries: 'Zobacz nieotagowane wpisy' 383 see_untagged_entries: 'Zobacz nieotagowane wpisy'
384 new:
385 add: 'Dodaj'
386 placeholder: 'Możesz dodać kilka tagów, oddzielając je przecinkami.'
332 387
333import: 388import:
334 page_title: 'Import' 389 page_title: 'Import'
@@ -362,6 +417,7 @@ import:
362 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.' 417 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.'
363 worker: 418 worker:
364 enabled: "Import jest wykonywany asynchronicznie. Od momentu rozpoczęcia importu, zewnętrzna usługa może zajmować się na raz tylko jednym zadaniem. Bieżącą usługą jest:" 419 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:"
420 download_images_warning: "Włączyłeś pobieranie obrazów dla swoich artykułów. W połączeniu z klasycznym importem, może to zająć dużo czasu (lub zakończyć się niepowodzeniem).<strong>Zdecydowanie zalecamy</strong> włączenie asynchronicznego importu, w celu uniknięcia błędów."
365 firefox: 421 firefox:
366 page_title: 'Import > Firefox' 422 page_title: 'Import > Firefox'
367 description: "Ten importer zaimportuje wszystkie twoje zakładki z Firefoksa. Idź do twoich zakładek (Ctrl+Shift+O), następnie w \"Import i kopie zapasowe\", wybierz \"Utwórz kopię zapasową...\". Uzyskasz plik .json." 423 description: "Ten importer zaimportuje wszystkie twoje zakładki z Firefoksa. Idź do twoich zakładek (Ctrl+Shift+O), następnie w \"Import i kopie zapasowe\", wybierz \"Utwórz kopię zapasową...\". Uzyskasz plik .json."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Import > Instapaper' 430 page_title: 'Import > Instapaper'
375 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Instapaper. W ustawieniach (https://www.instapaper.com/user), kliknij na "Download .CSV file" w sekcji "Export". Otrzymasz plik CSV.' 431 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Instapaper. W ustawieniach (https://www.instapaper.com/user), kliknij na "Download .CSV file" w sekcji "Export". Otrzymasz plik CSV.'
376 how_to: 'Wybierz swój plik eksportu z Instapaper i kliknij poniższy przycisk, aby go załadować.' 432 how_to: 'Wybierz swój plik eksportu z Instapaper i kliknij poniższy przycisk, aby go załadować.'
433 pinboard:
434 page_title: "Import > Pinboard"
435 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Pinboard. W ustawieniach kopii zapasowej (https://pinboard.in/settings/backup), kliknij na "JSON" w sekcji "Bookmarks". Otrzymasz plik "pinboard_export".'
436 how_to: 'Wybierz swój plik eksportu z Pinboard i kliknij poniższy przycisk, aby go załadować.'
377 437
378developer: 438developer:
379 page_title: 'Deweloper' 439 page_title: 'Deweloper'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: 'Jawne hasło' 504 plain_password_label: 'Jawne hasło'
445 email_label: 'Adres email' 505 email_label: 'Adres email'
446 enabled_label: 'Włączony' 506 enabled_label: 'Włączony'
447 locked_label: 'Zablokowany'
448 last_login_label: 'Ostatnie logowanie' 507 last_login_label: 'Ostatnie logowanie'
449 twofactor_label: Autoryzacja dwuetapowa 508 twofactor_label: Autoryzacja dwuetapowa
450 save: Zapisz 509 save: Zapisz
@@ -453,7 +512,7 @@ user:
453 back_to_list: Powrót do listy 512 back_to_list: Powrót do listy
454 513
455error: 514error:
456 # page_title: An error occurred 515 page_title: Wystąpił błąd
457 516
458flashes: 517flashes:
459 config: 518 config:
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'Informacje RSS zaktualizowane' 524 rss_updated: 'Informacje RSS zaktualizowane'
466 tagging_rules_updated: 'Reguły tagowania zaktualizowane' 525 tagging_rules_updated: 'Reguły tagowania zaktualizowane'
467 tagging_rules_deleted: 'Reguła tagowania usunięta' 526 tagging_rules_deleted: 'Reguła tagowania usunięta'
468 user_added: 'Użytkownik "%username%" dodany'
469 rss_token_updated: 'Token kanału RSS zaktualizowany' 527 rss_token_updated: 'Token kanału RSS zaktualizowany'
528 annotations_reset: Zresetuj adnotacje
529 tags_reset: Zresetuj tagi
530 entries_reset: Zresetuj wpisy
470 entry: 531 entry:
471 notice: 532 notice:
472 entry_already_saved: 'Wpis już został dodany %date%' 533 entry_already_saved: 'Wpis już został dodany %date%'
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 client_created: 'Nowy klient utworzony.' 558 client_created: 'Nowy klient utworzony.'
498 client_deleted: 'Klient usunięty' 559 client_deleted: 'Klient usunięty'
560 user:
561 notice:
562 added: 'Użytkownik "%username%" dodany'
563 updated: 'Użytkownik "%username%" zaktualizowany'
564 deleted: 'Użytkownik "%username%" usunięty'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
index a375ac7b..c4ace37c 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Posso ler ~200 palavras por minuto' 70 200_word: 'Posso ler ~200 palavras por minuto'
71 300_word: 'Posso ler ~300 palavras por minuto' 71 300_word: 'Posso ler ~300 palavras por minuto'
72 400_word: 'Posso ler ~400 palavras por minuto' 72 400_word: 'Posso ler ~400 palavras por minuto'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo' 77 pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo'
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-mail' 99 email_label: 'E-mail'
95 twoFactorAuthentication_label: 'Autenticação de dois passos' 100 twoFactorAuthentication_label: 'Autenticação de dois passos'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Senha atual' 116 old_password_label: 'Senha atual'
@@ -145,15 +162,16 @@ entry:
145 archived: 'Entradas arquivadas' 162 archived: 'Entradas arquivadas'
146 filtered: 'Entradas filtradas' 163 filtered: 'Entradas filtradas'
147 filtered_tags: 'Filtrar por tags:' 164 filtered_tags: 'Filtrar por tags:'
165 # filtered_search: 'Filtered by search:'
148 untagged: 'Entradas sem tags' 166 untagged: 'Entradas sem tags'
149 list: 167 list:
150 number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.' 168 number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.'
151 reading_time: 'tempo estimado de leitura' 169 reading_time: 'tempo estimado de leitura'
152 reading_time_minutes: 'tempo estimado de leitura: %readingTime% min' 170 reading_time_minutes: 'tempo estimado de leitura: %readingTime% min'
153 reading_time_less_one_minute: 'tempo estimado de leitura: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'tempo estimado de leitura: &lt; 1 min'
154 number_of_tags: '{1}e uma outra tag|]1,Inf[e %count% outras tags' 172 number_of_tags: '{1}e uma outra tag|]1,Inf[e %count% outras tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'Marcar como lido' 176 toogle_as_read: 'Marcar como lido'
159 toogle_as_star: 'Marcar como destacado' 177 toogle_as_star: 'Marcar como destacado'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Possui uma imagem de preview' 186 preview_picture_label: 'Possui uma imagem de preview'
169 preview_picture_help: 'Imagem de preview' 187 preview_picture_help: 'Imagem de preview'
170 language_label: 'Idioma' 188 language_label: 'Idioma'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Tempo de leitura em minutos' 191 label: 'Tempo de leitura em minutos'
173 from: 'de' 192 from: 'de'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 page_title: 'Editar uma entrada' 234 page_title: 'Editar uma entrada'
214 title_label: 'Título' 235 title_label: 'Título'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'How to' 274 page_title: 'How to'
254 page_description: 'Existem diferentes formas de salvar um artigo:' 275 page_description: 'Existem diferentes formas de salvar um artigo:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'Extensões de navegadores' 280 browser_addons: 'Extensões de navegadores'
257 mobile_apps: "App's móveis" 281 mobile_apps: "App's móveis"
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Extensão padrão do Firefox' 286 firefox: 'Extensão padrão do Firefox'
263 chrome: 'Extensão do Chrome' 287 chrome: 'Extensão do Chrome'
288 opera: 'Extensão do Opera'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'via F-Droid' 291 via_f_droid: 'via F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'na Microsoft Store' 294 windows: 'na Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Arraste e solve este link na sua barra de favoritos:' 296 description: 'Arraste e solve este link na sua barra de favoritos:'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 page_title: 'Começo Rápido' 326 page_title: 'Começo Rápido'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.' 382 number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.'
331 see_untagged_entries: 'Ver entradas sem tags' 383 see_untagged_entries: 'Ver entradas sem tags'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 page_title: 'Importar' 389 page_title: 'Importar'
@@ -362,6 +417,7 @@ import:
362 how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.' 417 how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.'
363 worker: 418 worker:
364 enabled: "A importação é feita assíncronamente. Uma vez que a tarefa de importação é iniciada, um trabalho externo pode executar tarefas uma por vez. O serviço atual é:" 419 enabled: "A importação é feita assíncronamente. Uma vez que a tarefa de importação é iniciada, um trabalho externo pode executar tarefas uma por vez. O serviço atual é:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 421 firefox:
366 page_title: 'Importar > Firefox' 422 page_title: 'Importar > Firefox'
367 description: "Com este importador você importa todos os favoritos de seu Firefox. Somente vá até seus favoritos (Ctrl+Maj+O), e em \"Importar e Backup\" e escolha \"Backup...\". Você terá então um arquivo .json." 423 description: "Com este importador você importa todos os favoritos de seu Firefox. Somente vá até seus favoritos (Ctrl+Maj+O), e em \"Importar e Backup\" e escolha \"Backup...\". Você terá então um arquivo .json."
@@ -374,6 +430,10 @@ import:
374 page_title: 'Importar > Instapaper' 430 page_title: 'Importar > Instapaper'
375 description: 'Este importador pode importar todos os artigos do seu Instapaper. Nas página de configurações (https://www.instapaper.com/user), clique em "Download .CSV file" na seção "Export". Um arquivo CSV será baixado (algo como "instapaper-export.csv").' 431 description: 'Este importador pode importar todos os artigos do seu Instapaper. Nas página de configurações (https://www.instapaper.com/user), clique em "Download .CSV file" na seção "Export". Um arquivo CSV será baixado (algo como "instapaper-export.csv").'
376 how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.' 432 how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.'
433 pinboard:
434 # page_title: "Import > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 page_title: 'Desenvolvedor' 439 page_title: 'Desenvolvedor'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'E-mail' 505 email_label: 'E-mail'
446 enabled_label: 'Habilitado' 506 enabled_label: 'Habilitado'
447 locked_label: 'Travado'
448 last_login_label: 'Último login' 507 last_login_label: 'Último login'
449 twofactor_label: 'Autenticação de dois passos' 508 twofactor_label: 'Autenticação de dois passos'
450 save: 'Salvar' 509 save: 'Salvar'
@@ -452,17 +511,23 @@ user:
452 delete_confirm: 'Tem certeza?' 511 delete_confirm: 'Tem certeza?'
453 back_to_list: 'Voltar para a lista' 512 back_to_list: 'Voltar para a lista'
454 513
514error:
515 # page_title: An error occurred
516
455flashes: 517flashes:
456 config: 518 config:
457 notice: 519 notice:
458 config_saved: 'Configiração salva.' 520 config_saved: 'Configiração salva.'
459 password_updated: 'Senha atualizada' 521 password_updated: 'Senha atualizada'
460 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.' 522 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.'
461 user_updated: 'Informação atualizada' 523 # user_updated: 'Information updated'
462 rss_updated: 'Informação de RSS atualizada' 524 rss_updated: 'Informação de RSS atualizada'
463 tagging_rules_updated: 'Regras de tags atualizadas' 525 tagging_rules_updated: 'Regras de tags atualizadas'
464 tagging_rules_deleted: 'Regra de tag apagada' 526 tagging_rules_deleted: 'Regra de tag apagada'
465 rss_token_updated: 'Token RSS atualizado' 527 rss_token_updated: 'Token RSS atualizado'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
466 entry: 531 entry:
467 notice: 532 notice:
468 entry_already_saved: 'Entrada já foi salva em %date%' 533 entry_already_saved: 'Entrada já foi salva em %date%'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index 7d8fcea3..928e5c27 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -70,7 +70,12 @@ config:
70 # 200_word: 'I read ~200 words per minute' 70 # 200_word: 'I read ~200 words per minute'
71 # 300_word: 'I read ~300 words per minute' 71 # 300_word: 'I read ~300 words per minute'
72 # 400_word: 'I read ~400 words per minute' 72 # 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket 77 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-mail' 99 email_label: 'E-mail'
95 # twoFactorAuthentication_label: 'Two factor authentication' 100 # twoFactorAuthentication_label: 'Two factor authentication'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Parola veche' 116 old_password_label: 'Parola veche'
@@ -145,15 +162,16 @@ entry:
145 # archived: 'Archived entries' 162 # archived: 'Archived entries'
146 # filtered: 'Filtered entries' 163 # filtered: 'Filtered entries'
147 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
148 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
149 list: 167 list:
150 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' 168 # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
151 reading_time: 'timp estimat de citire' 169 reading_time: 'timp estimat de citire'
152 reading_time_minutes: 'timp estimat de citire: %readingTime% min' 170 reading_time_minutes: 'timp estimat de citire: %readingTime% min'
153 reading_time_less_one_minute: 'timp estimat de citire: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'timp estimat de citire: &lt; 1 min'
154 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
155 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
156 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
157 original_article: 'original' 175 original_article: 'original'
158 toogle_as_read: 'Comută marcat ca citit' 176 toogle_as_read: 'Comută marcat ca citit'
159 toogle_as_star: 'Comută marcat ca favorit' 177 toogle_as_star: 'Comută marcat ca favorit'
@@ -168,6 +186,7 @@ entry:
168 preview_picture_label: 'Are o imagine de previzualizare' 186 preview_picture_label: 'Are o imagine de previzualizare'
169 preview_picture_help: 'Previzualizare imagine' 187 preview_picture_help: 'Previzualizare imagine'
170 language_label: 'Limbă' 188 language_label: 'Limbă'
189 # http_status_label: 'HTTP status'
171 reading_time: 190 reading_time:
172 label: 'Timp de citire în minute' 191 label: 'Timp de citire în minute'
173 from: 'de la' 192 from: 'de la'
@@ -209,6 +228,8 @@ entry:
209 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
210 form_new: 229 form_new:
211 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
212 edit: 233 edit:
213 # page_title: 'Edit an entry' 234 # page_title: 'Edit an entry'
214 # title_label: 'Title' 235 # title_label: 'Title'
@@ -252,6 +273,9 @@ about:
252howto: 273howto:
253 page_title: 'Cum să' 274 page_title: 'Cum să'
254 # page_description: 'There are several ways to save an article:' 275 # page_description: 'There are several ways to save an article:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
255 top_menu: 279 top_menu:
256 browser_addons: 'Add-On-uri de Browser' 280 browser_addons: 'Add-On-uri de Browser'
257 mobile_apps: 'Aplicații mobile' 281 mobile_apps: 'Aplicații mobile'
@@ -261,6 +285,7 @@ howto:
261 browser_addons: 285 browser_addons:
262 firefox: 'Add-On standard de Firefox' 286 firefox: 'Add-On standard de Firefox'
263 chrome: 'Extensie Chrome' 287 chrome: 'Extensie Chrome'
288 opera: 'Extensie Opera'
264 mobile_apps: 289 mobile_apps:
265 android: 290 android:
266 via_f_droid: 'prin F-Droid' 291 via_f_droid: 'prin F-Droid'
@@ -269,6 +294,33 @@ howto:
269 windows: 'prin Microsoft Store' 294 windows: 'prin Microsoft Store'
270 bookmarklet: 295 bookmarklet:
271 description: 'Drag &amp; drop acest link în bara de bookmark-uri:' 296 description: 'Drag &amp; drop acest link în bara de bookmark-uri:'
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
272 324
273quickstart: 325quickstart:
274 # page_title: 'Quickstart' 326 # page_title: 'Quickstart'
@@ -329,6 +381,9 @@ tag:
329 list: 381 list:
330 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 382 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
331 # see_untagged_entries: 'See untagged entries' 383 # see_untagged_entries: 'See untagged entries'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
332 387
333import: 388import:
334 # page_title: 'Import' 389 # page_title: 'Import'
@@ -362,6 +417,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 418 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 # 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:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 # firefox: 421 # firefox:
366 # page_title: 'Import > Firefox' 422 # page_title: 'Import > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +430,10 @@ import:
374 # page_title: 'Import > Instapaper' 430 # page_title: 'Import > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 # page_title: "Import > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 437
378developer: 438developer:
379 # page_title: 'Developer' 439 # page_title: 'Developer'
@@ -444,7 +504,6 @@ user:
444 plain_password_label: '????' 504 plain_password_label: '????'
445 email_label: 'E-mail' 505 email_label: 'E-mail'
446 # enabled_label: 'Enabled' 506 # enabled_label: 'Enabled'
447 # locked_label: 'Locked'
448 # last_login_label: 'Last login' 507 # last_login_label: 'Last login'
449 # twofactor_label: Two factor authentication 508 # twofactor_label: Two factor authentication
450 # save: Save 509 # save: Save
@@ -465,8 +524,10 @@ flashes:
465 rss_updated: 'Informație RSS actualizată' 524 rss_updated: 'Informație RSS actualizată'
466 # tagging_rules_updated: 'Tagging rules updated' 525 # tagging_rules_updated: 'Tagging rules updated'
467 # tagging_rules_deleted: 'Tagging rule deleted' 526 # tagging_rules_deleted: 'Tagging rule deleted'
468 # user_added: 'User "%username%" added'
469 # rss_token_updated: 'RSS token updated' 527 # rss_token_updated: 'RSS token updated'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
470 entry: 531 entry:
471 notice: 532 notice:
472 # entry_already_saved: 'Entry already saved on %date%' 533 # entry_already_saved: 'Entry already saved on %date%'
@@ -496,3 +557,8 @@ flashes:
496 notice: 557 notice:
497 # client_created: 'New client created.' 558 # client_created: 'New client created.'
498 # client_deleted: 'Client deleted' 559 # client_deleted: 'Client deleted'
560 user:
561 notice:
562 # added: 'User "%username%" added'
563 # updated: 'User "%username%" updated'
564 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
index 357aa2ae..d6cea974 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -70,7 +70,12 @@ config:
70 # 200_word: 'I read ~200 words per minute' 70 # 200_word: 'I read ~200 words per minute'
71 # 300_word: 'I read ~300 words per minute' 71 # 300_word: 'I read ~300 words per minute'
72 # 400_word: 'I read ~400 words per minute' 72 # 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 # pocket_consumer_key_label: Consumer key for Pocket to import contents 77 # pocket_consumer_key_label: Consumer key for Pocket to import contents
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-posta' 99 email_label: 'E-posta'
95 twoFactorAuthentication_label: 'İki adımlı doğrulama' 100 twoFactorAuthentication_label: 'İki adımlı doğrulama'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Eski şifre' 116 old_password_label: 'Eski şifre'
@@ -103,6 +120,7 @@ config:
103 # if_label: 'if' 120 # if_label: 'if'
104 # then_tag_as_label: 'then tag as' 121 # then_tag_as_label: 'then tag as'
105 # delete_rule_label: 'delete' 122 # delete_rule_label: 'delete'
123 # edit_rule_label: 'edit'
106 rule_label: 'Kural' 124 rule_label: 'Kural'
107 tags_label: 'Etiketler' 125 tags_label: 'Etiketler'
108 faq: 126 faq:
@@ -144,15 +162,16 @@ entry:
144 # archived: 'Archived entries' 162 # archived: 'Archived entries'
145 # filtered: 'Filtered entries' 163 # filtered: 'Filtered entries'
146 # filtered_tags: 'Filtered by tags:' 164 # filtered_tags: 'Filtered by tags:'
165 # filtered_search: 'Filtered by search:'
147 # untagged: 'Untagged entries' 166 # untagged: 'Untagged entries'
148 list: 167 list:
149 number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.' 168 number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.'
150 reading_time: 'tahmini okuma süresi' 169 reading_time: 'tahmini okuma süresi'
151 reading_time_minutes: 'tahmini okuma süresi: %readingTime% min' 170 reading_time_minutes: 'tahmini okuma süresi: %readingTime% min'
152 reading_time_less_one_minute: 'tahmini okuma süresi: <small class="inferieur">&lt;</small> 1 min' 171 reading_time_less_one_minute: 'tahmini okuma süresi: &lt; 1 min'
153 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags' 172 # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
154 reading_time_minutes_short: '%readingTime% min' 173 reading_time_minutes_short: '%readingTime% min'
155 reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min' 174 reading_time_less_one_minute_short: '&lt; 1 min'
156 original_article: 'orijinal' 175 original_article: 'orijinal'
157 toogle_as_read: 'Okundu/okunmadı olarak işaretle' 176 toogle_as_read: 'Okundu/okunmadı olarak işaretle'
158 toogle_as_star: 'Favorilere ekle/çıkar' 177 toogle_as_star: 'Favorilere ekle/çıkar'
@@ -167,6 +186,7 @@ entry:
167 preview_picture_label: 'Resim önizlemesi varsa' 186 preview_picture_label: 'Resim önizlemesi varsa'
168 preview_picture_help: 'Resim önizlemesi' 187 preview_picture_help: 'Resim önizlemesi'
169 language_label: 'Dil' 188 language_label: 'Dil'
189 # http_status_label: 'HTTP status'
170 reading_time: 190 reading_time:
171 label: 'Dakika cinsinden okuma süresi' 191 label: 'Dakika cinsinden okuma süresi'
172 from: 'başlangıç' 192 from: 'başlangıç'
@@ -208,6 +228,8 @@ entry:
208 placeholder: 'http://website.com' 228 placeholder: 'http://website.com'
209 form_new: 229 form_new:
210 url_label: Url 230 url_label: Url
231 search:
232 # placeholder: 'What are you looking for?'
211 edit: 233 edit:
212 page_title: 'Makaleyi düzenle' 234 page_title: 'Makaleyi düzenle'
213 title_label: 'Başlık' 235 title_label: 'Başlık'
@@ -251,6 +273,9 @@ about:
251howto: 273howto:
252 page_title: 'Yardım' 274 page_title: 'Yardım'
253 # page_description: 'There are several ways to save an article:' 275 # page_description: 'There are several ways to save an article:'
276 tab_menu:
277 # add_link: "Add a link"
278 # shortcuts: "Use shortcuts"
254 top_menu: 279 top_menu:
255 browser_addons: 'Tarayıcı eklentileri' 280 browser_addons: 'Tarayıcı eklentileri'
256 mobile_apps: 'Mobil uygulamalar' 281 mobile_apps: 'Mobil uygulamalar'
@@ -260,6 +285,7 @@ howto:
260 browser_addons: 285 browser_addons:
261 firefox: 'Standart Firefox Eklentisi' 286 firefox: 'Standart Firefox Eklentisi'
262 chrome: 'Chrome Eklentisi' 287 chrome: 'Chrome Eklentisi'
288 opera: 'Opera Eklentisi'
263 mobile_apps: 289 mobile_apps:
264 android: 290 android:
265 # via_f_droid: 'via F-Droid' 291 # via_f_droid: 'via F-Droid'
@@ -268,6 +294,33 @@ howto:
268 # windows: 'on the Microsoft Store' 294 # windows: 'on the Microsoft Store'
269 bookmarklet: 295 bookmarklet:
270 description: "Bu bağlantı ile yer imlerinizi sürükleyip bırakarak wallabag'e ekleyebilirsiniz:" 296 description: "Bu bağlantı ile yer imlerinizi sürükleyip bırakarak wallabag'e ekleyebilirsiniz:"
297 shortcuts:
298 # page_description: Here are the shortcuts available in wallabag.
299 # shortcut: Shortcut
300 # action: Action
301 # all_pages_title: Shortcuts available in all pages
302 # go_unread: Go to unread
303 # go_starred: Go to starred
304 # go_archive: Go to archive
305 # go_all: Go to all entries
306 # go_tags: Go to tags
307 # go_config: Go to config
308 # go_import: Go to import
309 # go_developers: Go to developers
310 # go_howto: Go to howto (this page!)
311 # go_logout: Logout
312 # list_title: Shortcuts available in listing pages
313 # search: Display the search form
314 # article_title: Shortcuts available in entry view
315 # open_original: Open original URL of the entry
316 # toggle_favorite: Toggle star status for the entry
317 # toggle_archive: Toggle read status for the entry
318 # delete: Delete the entry
319 # material_title: Shortcuts available with Material theme only
320 # add_link: Add a new link
321 # hide_form: Hide the current form (search or new link)
322 # arrows_navigation: Navigate through articles
323 # open_article: Display the selected entry
271 324
272quickstart: 325quickstart:
273 page_title: 'Hızlı başlangıç' 326 page_title: 'Hızlı başlangıç'
@@ -328,6 +381,9 @@ tag:
328 list: 381 list:
329 number_on_the_page: '{0} Herhangi bir etiket yok.|{1} Burada bir adet etiket var.|]1,Inf[ Burada %count% adet etiket var.' 382 number_on_the_page: '{0} Herhangi bir etiket yok.|{1} Burada bir adet etiket var.|]1,Inf[ Burada %count% adet etiket var.'
330 # see_untagged_entries: 'See untagged entries' 383 # see_untagged_entries: 'See untagged entries'
384 new:
385 # add: 'Add'
386 # placeholder: 'You can add several tags, separated by a comma.'
331 387
332import: 388import:
333 page_title: 'İçe Aktar' 389 page_title: 'İçe Aktar'
@@ -361,6 +417,7 @@ import:
361 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 417 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
362 worker: 418 worker:
363 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 419 # 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:"
420 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
364 firefox: 421 firefox:
365 page_title: 'İçe Aktar > Firefox' 422 page_title: 'İçe Aktar > Firefox'
366 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 423 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -373,6 +430,10 @@ import:
373 page_title: 'İçe Aktar > Instapaper' 430 page_title: 'İçe Aktar > Instapaper'
374 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 431 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
375 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 432 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
433 pinboard:
434 # page_title: "Import > Pinboard"
435 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
436 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
376 437
377developer: 438developer:
378 # page_title: 'Developer' 439 # page_title: 'Developer'
@@ -443,7 +504,6 @@ user:
443 plain_password_label: '????' 504 plain_password_label: '????'
444 email_label: 'E-posta' 505 email_label: 'E-posta'
445 # enabled_label: 'Enabled' 506 # enabled_label: 'Enabled'
446 # locked_label: 'Locked'
447 # last_login_label: 'Last login' 507 # last_login_label: 'Last login'
448 # twofactor_label: Two factor authentication 508 # twofactor_label: Two factor authentication
449 # save: Save 509 # save: Save
@@ -464,8 +524,10 @@ flashes:
464 rss_updated: 'RSS bilgiler güncellendi' 524 rss_updated: 'RSS bilgiler güncellendi'
465 tagging_rules_updated: 'Tagging rules updated' 525 tagging_rules_updated: 'Tagging rules updated'
466 tagging_rules_deleted: 'Tagging rule deleted' 526 tagging_rules_deleted: 'Tagging rule deleted'
467 user_added: 'User "%username%" added'
468 rss_token_updated: 'RSS token updated' 527 rss_token_updated: 'RSS token updated'
528 # annotations_reset: Annotations reset
529 # tags_reset: Tags reset
530 # entries_reset: Entries reset
469 entry: 531 entry:
470 notice: 532 notice:
471 entry_already_saved: 'Entry already saved on %date%' 533 entry_already_saved: 'Entry already saved on %date%'
@@ -495,3 +557,8 @@ flashes:
495 notice: 557 notice:
496 # client_created: 'New client created.' 558 # client_created: 'New client created.'
497 # client_deleted: 'Client deleted' 559 # client_deleted: 'Client deleted'
560 user:
561 notice:
562 # added: 'User "%username%" added'
563 # updated: 'User "%username%" updated'
564 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/views/base.html.twig b/src/Wallabag/CoreBundle/Resources/views/base.html.twig
index a1a9a136..289458d4 100644
--- a/src/Wallabag/CoreBundle/Resources/views/base.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/base.html.twig
@@ -41,6 +41,8 @@
41 {% block css %} 41 {% block css %}
42 {% endblock %} 42 {% endblock %}
43 {% block scripts %} 43 {% block scripts %}
44 <script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script>
45 <script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>
44 {% endblock %} 46 {% endblock %}
45 47
46 <title>{% block title %}{% endblock %} – wallabag</title> 48 <title>{% block title %}{% endblock %} – wallabag</title>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
index 98b0e119..3548f590 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
@@ -47,6 +47,14 @@
47 47
48 <fieldset class="w500p inline"> 48 <fieldset class="w500p inline">
49 <div class="row"> 49 <div class="row">
50 {{ form_label(form.config.action_mark_as_read) }}
51 {{ form_errors(form.config.action_mark_as_read) }}
52 {{ form_widget(form.config.action_mark_as_read) }}
53 </div>
54 </fieldset>
55
56 <fieldset class="w500p inline">
57 <div class="row">
50 {{ form_label(form.config.language) }} 58 {{ form_label(form.config.language) }}
51 {{ form_errors(form.config.language) }} 59 {{ form_errors(form.config.language) }}
52 {{ form_widget(form.config.language) }} 60 {{ form_widget(form.config.language) }}
@@ -71,6 +79,19 @@
71 </a> 79 </a>
72 </fieldset> 80 </fieldset>
73 81
82 <fieldset class="w500p inline">
83 <div class="row">
84 <h3>{{ 'config.form_settings.android_configuration'|trans }}</h3>
85 <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" >Touch here to prefill your Android application</a>
86 <br/>
87 <img id="androidQrcode" />
88 <script>
89 const imgBase64 = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
90 document.getElementById('androidQrcode').src = imgBase64;
91 </script>
92 </div>
93 </fieldset>
94
74 {{ form_rest(form.config) }} 95 {{ form_rest(form.config) }}
75 </form> 96 </form>
76 97
@@ -164,10 +185,41 @@
164 </fieldset> 185 </fieldset>
165 {% endif %} 186 {% endif %}
166 187
188 <h2>{{ 'config.reset.title'|trans }}</h2>
189 <fieldset class="w500p inline">
190 <p>{{ 'config.reset.description'|trans }}</p>
191 <ul>
192 <li>
193 <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
194 {{ 'config.reset.annotations'|trans }}
195 </a>
196 </li>
197 <li>
198 <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
199 {{ 'config.reset.tags'|trans }}
200 </a>
201 </li>
202 <li>
203 <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
204 {{ 'config.reset.entries'|trans }}
205 </a>
206 </li>
207 </ul>
208 </fieldset>
209
167 {{ form_widget(form.user._token) }} 210 {{ form_widget(form.user._token) }}
168 {{ form_widget(form.user.save) }} 211 {{ form_widget(form.user.save) }}
169 </form> 212 </form>
170 213
214 {% if enabled_users > 1 %}
215 <h2>{{ 'config.form_user.delete.title'|trans }}</h2>
216
217 <p>{{ 'config.form_user.delete.description'|trans }}</p>
218 <a href="{{ path('delete_account') }}" onclick="return confirm('{{ 'config.form_user.delete.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red delete-account">
219 {{ 'config.form_user.delete.button'|trans }}
220 </a>
221 {% endif %}
222
171 <h2>{{ 'config.tab_menu.password'|trans }}</h2> 223 <h2>{{ 'config.tab_menu.password'|trans }}</h2>
172 224
173 {{ form_start(form.pwd) }} 225 {{ form_start(form.pwd) }}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
index 4679714e..859b166b 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
@@ -1,20 +1,25 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %} 1{% extends "WallabagCoreBundle::layout.html.twig" %}
2 2
3{% block title %} 3{% block title %}
4 {% set currentTag = '' %} 4 {% set filter = '' %}
5 {% if tag is defined %} 5 {% if tag is defined %}
6 {% set currentTag = tag %} 6 {% set filter = tag %}
7 {% endif %} 7 {% endif %}
8 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %} 8 {% if searchTerm is defined and searchTerm is not empty %}
9 {% set filter = searchTerm %}
10 {% endif %}
11 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'filter': filter} %}
9{% endblock %} 12{% endblock %}
10 13
11{% block content %} 14{% block content %}
12 15
16 {% set listMode = app.user.config.listMode %}
13 <div class="results"> 17 <div class="results">
14 <div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div> 18 <div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div>
15 <div class="pagination"> 19 <div class="pagination">
16 <i class="btn-clickable download-btn material-icons md-36">file_download</i> 20 <a href="{{ path('switch_view_mode') }}"><i class="listMode-btn material-icons md-36">{% if listMode == 0 %}list{% else %}view_module{% endif %}</i></a>
17 <i class="btn-clickable filter-btn material-icons md-36">filter_list</i> 21 <i class="btn-clickable download-btn material-icons md-36 js-export-action">file_download</i>
22 <i class="btn-clickable filter-btn material-icons md-36 js-filters-action">filter_list</i>
18 {% if entries.getNbPages > 1 %} 23 {% if entries.getNbPages > 1 %}
19 {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} 24 {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
20 {% endif %} 25 {% endif %}
@@ -22,7 +27,7 @@
22 </div> 27 </div>
23 28
24 {% for entry in entries %} 29 {% for entry in entries %}
25 <div id="entry-{{ entry.id|e }}" class="entry"> 30 <div id="entry-{{ entry.id|e }}" class="{% if listMode == 0 %}entry{% else %}listmode entry{% endif %}">
26 <h2><a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title|e|raw }}">{{ entry.title|e|raw }}</a></h2> 31 <h2><a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title|e|raw }}">{{ entry.title|e|raw }}</a></h2>
27 32
28 {% set readingTime = entry.readingTime / app.user.config.readingSpeed %} 33 {% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
@@ -47,13 +52,13 @@
47 <li><a title="{{ 'entry.list.delete'|trans }}" class="tool delete icon-trash icon" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.list.delete'|trans }}</span></a></li> 52 <li><a title="{{ 'entry.list.delete'|trans }}" class="tool delete icon-trash icon" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.list.delete'|trans }}</span></a></li>
48 <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon-link icon"><span>{{ entry.domainName|removeWww }}</span></a></li> 53 <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon-link icon"><span>{{ entry.domainName|removeWww }}</span></a></li>
49 </ul> 54 </ul>
50 {% if entry.previewPicture is null %} 55 {% if (entry.previewPicture is null or listMode == 1) %}
51 <ul class="card-entry-tags"> 56 <ul class="card-entry-tags">
52 {% for tag in entry.tags %} 57 {% for tag in entry.tags %}
53 <li><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a></li> 58 <li><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a></li>
54 {% endfor %} 59 {% endfor %}
55 </ul> 60 </ul>
56 <p>{{ entry.content|striptags|slice(0, 300) }}&hellip;</p> 61 <p {% if listMode == 1 %}class="hide"{% endif %}>{{ entry.content|striptags|slice(0, 300) }}&hellip;</p>
57 {% else %} 62 {% else %}
58 <ul class="card-entry-labels"> 63 <ul class="card-entry-labels">
59 {% for tag in entry.tags | slice(0, 3) %} 64 {% for tag in entry.tags | slice(0, 3) %}
@@ -131,6 +136,13 @@
131 </div> 136 </div>
132 </div> 137 </div>
133 138
139 <div id="filter-http-status" class="filter-group">
140 {{ form_label(form.httpStatus) }}
141 <div class="input-field ">
142 {{ form_widget(form.httpStatus) }}
143 </div>
144 </div>
145
134 <div id="filter-reading-time" class="filter-group"> 146 <div id="filter-reading-time" class="filter-group">
135 <div class=""> 147 <div class="">
136 {{ form_label(form.readingTime) }} 148 {{ form_label(form.readingTime) }}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
index 8ca194f6..a555691d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
@@ -11,7 +11,7 @@
11 <div id="article_toolbar"> 11 <div id="article_toolbar">
12 <ul class="links"> 12 <ul class="links">
13 <li class="topPosF"><a href="#top" title="{{ 'entry.view.left_menu.back_to_top'|trans }}" class="tool top icon icon-arrow-up-thick"><span>{{ 'entry.view.left_menu.set_as_read'|trans }}</span></a></li> 13 <li class="topPosF"><a href="#top" title="{{ 'entry.view.left_menu.back_to_top'|trans }}" class="tool top icon icon-arrow-up-thick"><span>{{ 'entry.view.left_menu.set_as_read'|trans }}</span></a></li>
14 <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link"><span>{{ entry.domainName|removeWww }}</span></a></li> 14 <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link original"><span>{{ entry.domainName|removeWww }}</span></a></li>
15 <li><a title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" class="tool icon icon-reload" href="{{ path('reload_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span></a></li> 15 <li><a title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" class="tool icon icon-reload" href="{{ path('reload_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span></a></li>
16 16
17 {% set markAsReadLabel = 'entry.view.left_menu.set_as_unread' %} 17 {% set markAsReadLabel = 'entry.view.left_menu.set_as_unread' %}
@@ -19,8 +19,8 @@
19 {% set markAsReadLabel = 'entry.view.left_menu.set_as_read' %} 19 {% set markAsReadLabel = 'entry.view.left_menu.set_as_read' %}
20 {% endif %} 20 {% endif %}
21 21
22 <li><a title="{{ markAsReadLabel|trans }}" class="tool icon icon-check {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %}" href="{{ path('archive_entry', { 'id': entry.id }) }}"><span>{{ markAsReadLabel|trans }}</span></a></li> 22 <li><a title="{{ markAsReadLabel|trans }}" class="tool icon icon-check {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %} markasread" href="{{ path('archive_entry', { 'id': entry.id }) }}"><span>{{ markAsReadLabel|trans }}</span></a></li>
23 <li><a title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" class="tool icon icon-star {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span></a></li> 23 <li><a title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" class="tool icon icon-star {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %} favorite" href="{{ path('star_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span></a></li>
24 <li><a id="nav-btn-add-tag" class="tool icon icon-price-tags" title="{{ 'entry.view.left_menu.add_a_tag'|trans }}"><span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span></a></li> 24 <li><a id="nav-btn-add-tag" class="tool icon icon-price-tags" title="{{ 'entry.view.left_menu.add_a_tag'|trans }}"><span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span></a></li>
25 <li><a title="{{ 'entry.view.left_menu.delete'|trans }}" class="tool delete icon icon-trash" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.delete'|trans }}</span></a></li> 25 <li><a title="{{ 'entry.view.left_menu.delete'|trans }}" class="tool delete icon icon-trash" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.delete'|trans }}</span></a></li>
26 {% if craue_setting('share_public') %} 26 {% if craue_setting('share_public') %}
@@ -29,8 +29,9 @@
29 {% endif %} 29 {% endif %}
30 {% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %} 30 {% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %}
31 {% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %} 31 {% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %}
32 {% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %} 32 {% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}" target="_blank" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
33 {% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %} 33 {% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %}
34 {% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %}
34 {% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" title="carrot"><span>Carrot</span></a></li>{% endif %} 35 {% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" title="carrot"><span>Carrot</span></a></li>{% endif %}
35 {% if craue_setting('show_printlink') %}<li><a title="{{ 'entry.view.left_menu.print'|trans }}" class="tool icon icon-print" href="javascript: window.print();"><span>{{ 'entry.view.left_menu.print'|trans }}</span></a></li>{% endif %} 36 {% if craue_setting('show_printlink') %}<li><a title="{{ 'entry.view.left_menu.print'|trans }}" class="tool icon icon-print" href="javascript: window.print();"><span>{{ 'entry.view.left_menu.print'|trans }}</span></a></li>{% endif %}
36 {% if craue_setting('export_epub') %}<li><a href="?epub&amp;method=id&amp;value={{ entry.id }}" title="Generate ePub file">EPUB</a></li>{% endif %} 37 {% if craue_setting('export_epub') %}<li><a href="?epub&amp;method=id&amp;value={{ entry.id }}" title="Generate ePub file">EPUB</a></li>{% endif %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig
new file mode 100644
index 00000000..20821b6d
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/search_form.html.twig
@@ -0,0 +1,17 @@
1<form name="search" method="GET" action="{{ path('search')}}">
2 <h2>{{ 'menu.left.search'|trans }}</h2>
3 <a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">&times;</a>
4 {% if form_errors(form) %}
5 <span class="black-text">{{ form_errors(form) }}</span>
6 {% endif %}
7
8 {% if form_errors(form.term) %}
9 <span class="black-text">{{ form_errors(form.term) }}</span>
10 {% endif %}
11
12 <input type="hidden" name="currentRoute" value="{{ currentRoute }}" />
13
14 {{ form_widget(form.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'entry.search.placeholder'} }) }}
15
16 {{ form_rest(form) }}
17</form>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
index cd4ed3fa..07ff8e14 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
@@ -31,17 +31,11 @@
31 <li class="menu all"><a href="{{ path('all') }}">{{ 'menu.left.all_articles'|trans }}</a></li> 31 <li class="menu all"><a href="{{ path('all') }}">{{ 'menu.left.all_articles'|trans }}</a></li>
32 <li class="menu tag"><a href="{{ path('tag') }}">{{ 'menu.left.tags'|trans }}</a></li> 32 <li class="menu tag"><a href="{{ path('tag') }}">{{ 'menu.left.tags'|trans }}</a></li>
33 <li class="menu new"><a href="{{ path('new') }}">{{ 'menu.left.save_link'|trans }}</a></li> 33 <li class="menu new"><a href="{{ path('new') }}">{{ 'menu.left.save_link'|trans }}</a></li>
34 <!--<li style="position: relative;"><a href="javascript: void(null);" id="search">{{ 'menu.left.search'|trans }}</a> 34 <li style="position: relative;"><a href="javascript: void(null);" id="search">{{ 'menu.left.search'|trans }}</a>
35 <div id="search-form" class="messages info popup-form"> 35 <div id="search-form" class="messages info popup-form">
36 <form method="get" action="index.php"> 36 {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
37 <h2>{{ 'menu.left.search'|trans }}</h2>
38 <a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">&times;</a>
39 <input type="hidden" name="view" value="search">
40 <input required placeholder="{{ 'menu.search_form.input_label'|trans }}" type="text" name="search" id="searchfield"><br>
41 <input id="submit-search" type="submit" value="{{ 'menu.left.search'|trans }}">
42 </form>
43 </div> 37 </div>
44 </li>--> 38 </li>
45 <li class="menu config"><a href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a></li> 39 <li class="menu config"><a href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a></li>
46 {% if is_granted('ROLE_SUPER_ADMIN') %} 40 {% if is_granted('ROLE_SUPER_ADMIN') %}
47 <li class="menu users"><a href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a></li> 41 <li class="menu users"><a href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a></li>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig
index 92cabdd9..654c1d2d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig
@@ -6,8 +6,10 @@
6 {{ 'entry.page_titles.archived'|trans }} 6 {{ 'entry.page_titles.archived'|trans }}
7{% elseif currentRoute == 'all' %} 7{% elseif currentRoute == 'all' %}
8 {{ 'entry.page_titles.filtered'|trans }} 8 {{ 'entry.page_titles.filtered'|trans }}
9{% elseif currentRoute == 'search' %}
10 {{ 'entry.page_titles.filtered_search'|trans }} {{ filter }}
9{% elseif currentRoute == 'tag_entries' %} 11{% elseif currentRoute == 'tag_entries' %}
10 {{ 'entry.page_titles.filtered_tags'|trans }} {{ currentTag }} 12 {{ 'entry.page_titles.filtered_tags'|trans }} {{ filter }}
11{% elseif currentRoute == 'untagged' %} 13{% elseif currentRoute == 'untagged' %}
12 {{ 'entry.page_titles.untagged'|trans }} 14 {{ 'entry.page_titles.untagged'|trans }}
13{% else %} 15{% else %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
index 7103f22b..12e8c79f 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig
@@ -2,7 +2,15 @@
2<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/"> 2<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
3 <channel> 3 <channel>
4 <title>wallabag — {{type}} feed</title> 4 <title>wallabag — {{type}} feed</title>
5 <link>{{ url('unread') }}</link> 5 <link>{{ url(type) }}</link>
6 <link rel="self" href="{{ app.request.uri }}"/>
7 {% if entries.hasPreviousPage -%}
8 <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/>
9 {% endif -%}
10 {% if entries.hasNextPage -%}
11 <link rel="next" href="{{ url }}?page={{ entries.nextPage }}"/>
12 {% endif -%}
13 <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/>
6 <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> 14 <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate>
7 <generator>wallabag</generator> 15 <generator>wallabag</generator>
8 <description>wallabag {{type}} elements</description> 16 <description>wallabag {{type}} elements</description>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig
index 67a10190..231f9bdf 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig
@@ -7,38 +7,186 @@
7 <div class="row"> 7 <div class="row">
8 <div class="col s12"> 8 <div class="col s12">
9 <div class="card-panel settings"> 9 <div class="card-panel settings">
10 <p>{{ 'howto.page_description'|trans }}</p>
11 10
12 <div class="row"> 11 <div class="row">
13 12 <div class="div_tabs col s12">
14 <div class="col s12"> 13 <ul class="tabs">
15 <a href="{{ path('new') }}">{{ 'howto.form.description'|trans }}</a> 14 <li class="tab col s12 m6 l3"><a class="active" href="#set1">{{ 'howto.tab_menu.add_link'|trans }}</a></li>
16 </div> 15 <li class="tab col s12 m6 l3"><a href="#set2">{{ 'howto.tab_menu.shortcuts'|trans }}</a></li>
17
18 <div class="col s12">
19 <h5>{{ 'howto.top_menu.browser_addons'|trans }}</h5>
20 <ul>
21 <li><a href="{{ addonsUrl.firefox }}" target="_blank">{{ 'howto.browser_addons.firefox'|trans }}</a></li>
22 <li><a href="{{ addonsUrl.chrome }}" target="_blank">{{ 'howto.browser_addons.chrome'|trans }}</a></li>
23 </ul> 16 </ul>
24 </div> 17 </div>
25 18
26 <div class="col s12"> 19 <div id="set1" class="col s12">
27 <h5>{{ 'howto.top_menu.mobile_apps'|trans }}</h5> 20 <p>{{ 'howto.page_description'|trans }}</p>
28 <ul> 21
29 <li>Android: <a href="{{ addonsUrl.f_droid }}" target="_blank">{{ 'howto.mobile_apps.android.via_f_droid'|trans }}</a> / <a href="{{ addonsUrl.google_play }}" target="_blank">{{ 'howto.mobile_apps.android.via_google_play'|trans }}</a></li> 22 <div class="row">
30 <li>iOS: <a href="{{ addonsUrl.ios }}" target="_blank">{{ 'howto.mobile_apps.ios'|trans }}</a></li>
31 <li>Windows Phone: <a href="{{ addonsUrl.windows }}" target="_blank">{{ 'howto.mobile_apps.windows'|trans }}</a></li>
32 </ul>
33 </div>
34 23
35 <div class="col s12"> 24 <div class="col s12">
36 <h5>{{ 'howto.top_menu.bookmarklet'|trans }}</h5> 25 <a href="{{ path('new') }}">{{ 'howto.form.description'|trans }}</a>
37 {{ 'howto.bookmarklet.description'|trans }} 26 </div>
38 {% include '@WallabagCore/themes/common/Static/_bookmarklet.html.twig' %} 27
28 <div class="col s12">
29 <h5>{{ 'howto.top_menu.browser_addons'|trans }}</h5>
30 <ul>
31 <li><a href="{{ addonsUrl.firefox }}" target="_blank">{{ 'howto.browser_addons.firefox'|trans }}</a></li>
32 <li><a href="{{ addonsUrl.chrome }}" target="_blank">{{ 'howto.browser_addons.chrome'|trans }}</a></li>
33 <li><a href="{{ addonsUrl.opera }}" target="_blank">{{ 'howto.browser_addons.opera'|trans }}</a></li>
34 </ul>
35 </div>
36
37 <div class="col s12">
38 <h5>{{ 'howto.top_menu.mobile_apps'|trans }}</h5>
39 <ul>
40 <li>Android: <a href="{{ addonsUrl.f_droid }}" target="_blank">{{ 'howto.mobile_apps.android.via_f_droid'|trans }}</a> / <a href="{{ addonsUrl.google_play }}" target="_blank">{{ 'howto.mobile_apps.android.via_google_play'|trans }}</a></li>
41 <li>iOS: <a href="{{ addonsUrl.ios }}" target="_blank">{{ 'howto.mobile_apps.ios'|trans }}</a></li>
42 <li>Windows Phone: <a href="{{ addonsUrl.windows }}" target="_blank">{{ 'howto.mobile_apps.windows'|trans }}</a></li>
43 </ul>
44 </div>
45
46 <div class="col s12">
47 <h5>{{ 'howto.top_menu.bookmarklet'|trans }}</h5>
48 {{ 'howto.bookmarklet.description'|trans }}
49 {% include '@WallabagCore/themes/common/Static/_bookmarklet.html.twig' %}
50 </div>
51
52 </div>
39 </div> 53 </div>
40 54
41 </div> 55 <div id="set2" class="col s12">
56 <p>{{ 'howto.shortcuts.page_description'|trans }}</p>
57
58 <h5>{{ 'howto.shortcuts.all_pages_title'|trans }}</h5>
59
60 <table class="bordered">
61 <thead>
62 <tr>
63 <th>{{ 'howto.shortcuts.shortcut'|trans }}</th>
64 <th>{{ 'howto.shortcuts.action'|trans }}</th>
65 </tr>
66 </thead>
67
68 <tbody>
69 <tr>
70 <td><code>g u</code></td>
71 <td>{{ 'howto.shortcuts.go_unread'|trans }}</td>
72 </tr>
73 <tr>
74 <td><code>g s</code></td>
75 <td>{{ 'howto.shortcuts.go_starred'|trans }}</td>
76 </tr>
77 <tr>
78 <td><code>g r</code></td>
79 <td>{{ 'howto.shortcuts.go_archive'|trans }}</td>
80 </tr>
81 <tr>
82 <td><code>g a</code></td>
83 <td>{{ 'howto.shortcuts.go_all'|trans }}</td>
84 </tr>
85 <tr>
86 <td><code>g t</code></td>
87 <td>{{ 'howto.shortcuts.go_tags'|trans }}</td>
88 </tr>
89 <tr>
90 <td><code>g c</code></td>
91 <td>{{ 'howto.shortcuts.go_config'|trans }}</td>
92 </tr>
93 <tr>
94 <td><code>g i</code></td>
95 <td>{{ 'howto.shortcuts.go_import'|trans }}</td>
96 </tr>
97 <tr>
98 <td><code>g d</code></td>
99 <td>{{ 'howto.shortcuts.go_developers'|trans }}</td>
100 </tr>
101 <tr>
102 <td><code>?</code></td>
103 <td>{{ 'howto.shortcuts.go_howto'|trans }}</td>
104 </tr>
105 <tr>
106 <td><code>g l</code></td>
107 <td>{{ 'howto.shortcuts.go_logout'|trans }}</td>
108 </tr>
109 </tbody>
110 </table>
111
112 <h5>{{ 'howto.shortcuts.list_title'|trans }}</h5>
113
114 <table class="bordered">
115 <thead>
116 <tr>
117 <th>{{ 'howto.shortcuts.shortcut'|trans }}</th>
118 <th>{{ 'howto.shortcuts.action'|trans }}</th>
119 </tr>
120 </thead>
121
122 <tbody>
123 <tr>
124 <td><code>s</code></td>
125 <td>{{ 'howto.shortcuts.search'|trans }}</td>
126 </tr>
127 </tbody>
128 </table>
129
130 <h5>{{ 'howto.shortcuts.article_title'|trans }}</h5>
131
132 <table class="bordered">
133 <thead>
134 <tr>
135 <th>{{ 'howto.shortcuts.shortcut'|trans }}</th>
136 <th>{{ 'howto.shortcuts.action'|trans }}</th>
137 </tr>
138 </thead>
139
140 <tbody>
141 <tr>
142 <td><code>o</code></td>
143 <td>{{ 'howto.shortcuts.open_original'|trans }}</td>
144 </tr>
145 <tr>
146 <td><code>f</code></td>
147 <td>{{ 'howto.shortcuts.toggle_favorite'|trans }}</td>
148 </tr>
149 <tr>
150 <td><code>a</code></td>
151 <td>{{ 'howto.shortcuts.toggle_archive'|trans }}</td>
152 </tr>
153 <tr>
154 <td><code>del</code></td>
155 <td>{{ 'howto.shortcuts.delete'|trans }}</td>
156 </tr>
157 </tbody>
158 </table>
159
160 <h5>{{ 'howto.shortcuts.material_title'|trans }}</h5>
161
162 <table class="bordered">
163 <thead>
164 <tr>
165 <th>{{ 'howto.shortcuts.shortcut'|trans }}</th>
166 <th>{{ 'howto.shortcuts.action'|trans }}</th>
167 </tr>
168 </thead>
169
170 <tbody>
171 <tr>
172 <td><code>g n</code></td>
173 <td>{{ 'howto.shortcuts.add_link'|trans }}</td>
174 </tr>
175 <tr>
176 <td><code>esc</code></td>
177 <td>{{ 'howto.shortcuts.hide_form'|trans }}</td>
178 </tr>
179 <tr>
180 <td><code>← →</code></td>
181 <td>{{ 'howto.shortcuts.arrows_navigation'|trans }}</td>
182 </tr>
183 <tr>
184 <td><code>enter</code></td>
185 <td>{{ 'howto.shortcuts.open_article'|trans }}</td>
186 </tr>
187 </tbody>
188 </table>
189 </div>
42 190
43 </div> 191 </div>
44 </div> 192 </div>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig
index c8a303a6..b7a48551 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig
@@ -15,7 +15,7 @@
15 <li class="col l4 m6 s12"> 15 <li class="col l4 m6 s12">
16 <div class="card teal darken-1"> 16 <div class="card teal darken-1">
17 <div class="card-content white-text"> 17 <div class="card-content white-text">
18 <span class="card-title">{{ 'quickstart.configure.title'|trans }}</span> 18 <span class="card-title white-text">{{ 'quickstart.configure.title'|trans }}</span>
19 <p>{{ 'quickstart.configure.description'|trans }}</p> 19 <p>{{ 'quickstart.configure.description'|trans }}</p>
20 </div> 20 </div>
21 <div class="card-action"> 21 <div class="card-action">
@@ -31,7 +31,7 @@
31 <li class="col l4 m6 s12"> 31 <li class="col l4 m6 s12">
32 <div class="card green darken-1"> 32 <div class="card green darken-1">
33 <div class="card-content white-text"> 33 <div class="card-content white-text">
34 <span class="card-title">{{ 'quickstart.first_steps.title'|trans }}</span> 34 <span class="card-title white-text">{{ 'quickstart.first_steps.title'|trans }}</span>
35 <p>{{ 'quickstart.first_steps.description'|trans }}</p> 35 <p>{{ 'quickstart.first_steps.description'|trans }}</p>
36 </div> 36 </div>
37 <div class="card-action"> 37 <div class="card-action">
@@ -46,7 +46,7 @@
46 <li class="col l4 m6 s12"> 46 <li class="col l4 m6 s12">
47 <div class="card light-green darken-1"> 47 <div class="card light-green darken-1">
48 <div class="card-content white-text"> 48 <div class="card-content white-text">
49 <span class="card-title">{{ 'quickstart.migrate.title'|trans }}</span> 49 <span class="card-title white-text">{{ 'quickstart.migrate.title'|trans }}</span>
50 <p>{{ 'quickstart.migrate.description'|trans }}</p> 50 <p>{{ 'quickstart.migrate.description'|trans }}</p>
51 </div> 51 </div>
52 <div class="card-action"> 52 <div class="card-action">
@@ -63,7 +63,7 @@
63 <li class="col l4 m6 s12"> 63 <li class="col l4 m6 s12">
64 <div class="card blue darken-1"> 64 <div class="card blue darken-1">
65 <div class="card-content white-text"> 65 <div class="card-content white-text">
66 <span class="card-title">{{ 'quickstart.developer.title'|trans }}</span> 66 <span class="card-title white-text">{{ 'quickstart.developer.title'|trans }}</span>
67 <p>{{ 'quickstart.developer.description'|trans }}</p> 67 <p>{{ 'quickstart.developer.description'|trans }}</p>
68 </div> 68 </div>
69 <div class="card-action"> 69 <div class="card-action">
@@ -79,7 +79,7 @@
79 <li class="col l4 m6 s12"> 79 <li class="col l4 m6 s12">
80 <div class="card light-blue darken-1"> 80 <div class="card light-blue darken-1">
81 <div class="card-content white-text"> 81 <div class="card-content white-text">
82 <span class="card-title">{{ 'quickstart.docs.title'|trans }}</span> 82 <span class="card-title white-text">{{ 'quickstart.docs.title'|trans }}</span>
83 <p>{{ 'quickstart.docs.description'|trans }}</p> 83 <p>{{ 'quickstart.docs.description'|trans }}</p>
84 </div> 84 </div>
85 <div class="card-action"> 85 <div class="card-action">
@@ -95,7 +95,7 @@
95 <li class="col l4 m6 s12"> 95 <li class="col l4 m6 s12">
96 <div class="card cyan darken-1"> 96 <div class="card cyan darken-1">
97 <div class="card-content white-text"> 97 <div class="card-content white-text">
98 <span class="card-title">{{ 'quickstart.support.title'|trans }}</span> 98 <span class="card-title white-text">{{ 'quickstart.support.title'|trans }}</span>
99 <p>{{ 'quickstart.support.description'|trans }}</p> 99 <p>{{ 'quickstart.support.description'|trans }}</p>
100 </div> 100 </div>
101 <div class="card-action"> 101 <div class="card-action">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
index 9f67217b..5d411fdd 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
@@ -66,6 +66,14 @@
66 </div> 66 </div>
67 </div> 67 </div>
68 68
69 <div class="row">
70 <div class="input-field col s12">
71 {{ form_label(form.config.action_mark_as_read) }}
72 {{ form_errors(form.config.action_mark_as_read) }}
73 {{ form_widget(form.config.action_mark_as_read) }}
74 </div>
75 </div>
76
69 <div class="row"> 77 <div class="row">
70 <div class="input-field col s11"> 78 <div class="input-field col s11">
71 {{ form_label(form.config.language) }} 79 {{ form_label(form.config.language) }}
@@ -96,6 +104,18 @@
96 </div> 104 </div>
97 </div> 105 </div>
98 106
107 <div class="row">
108 <div class="input-field col s12">
109 <h5>{{ 'config.form_settings.android_configuration'|trans }}</h5>
110 <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" class="waves-effect waves-light btn hide-on-large-only">Touch here to prefill your Android application</a>
111 <img id="androidQrcode" class="hide-on-med-and-down" />
112 </div>
113 <script>
114 const imgBase64 = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
115 document.getElementById('androidQrcode').src = imgBase64;
116 </script>
117 </div>
118
99 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 119 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
100 {{ form_rest(form.config) }} 120 {{ form_rest(form.config) }}
101 </form> 121 </form>
@@ -197,6 +217,34 @@
197 {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 217 {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
198 {{ form_widget(form.user._token) }} 218 {{ form_widget(form.user._token) }}
199 </form> 219 </form>
220
221 <br /><hr /><br />
222
223 <div class="row">
224 <h5>{{ 'config.reset.title'|trans }}</h5>
225 <p>{{ 'config.reset.description'|trans }}</p>
226 <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
227 {{ 'config.reset.annotations'|trans }}
228 </a>
229 <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
230 {{ 'config.reset.tags'|trans }}
231 </a>
232 <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
233 {{ 'config.reset.entries'|trans }}
234 </a>
235 </div>
236
237 {% if enabled_users > 1 %}
238 <br /><hr /><br />
239
240 <div class="row">
241 <h5>{{ 'config.form_user.delete.title'|trans }}</h5>
242 <p>{{ 'config.form_user.delete.description'|trans }}</p>
243 <a href="{{ path('delete_account') }}" onclick="return confirm('{{ 'config.form_user.delete.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red delete-account">
244 {{ 'config.form_user.delete.button'|trans }}
245 </a>
246 </div>
247 {% endif %}
200 </div> 248 </div>
201 249
202 <div id="set4" class="col s12"> 250 <div id="set4" class="col s12">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_actions.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_actions.html.twig
index 56442116..d278da1b 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_actions.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_actions.html.twig
@@ -1,8 +1,6 @@
1<div class="card-action"> 1<div class="card-action">
2 <span class="reading-time grey-text"> 2 <span class="reading-time grey-text">
3 <i class="material-icons" title="{{ 'entry.list.reading_time'|trans }}">timer</i> 3 {% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %}
4 {{ entry.readingTime / app.user.config.readingSpeed|round }} min&nbsp;
5
6 <i class="material-icons hide-on-med-and-down" title="{{ 'entry.view.created_at'|trans }}">today</i> 4 <i class="material-icons hide-on-med-and-down" title="{{ 'entry.view.created_at'|trans }}">today</i>
7 <span class="hide-on-med-and-down">&nbsp;{{ entry.createdAt|date('Y-m-d') }}</span> 5 <span class="hide-on-med-and-down">&nbsp;{{ entry.createdAt|date('Y-m-d') }}</span>
8 </span> 6 </span>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig
new file mode 100644
index 00000000..bb9b64ce
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig
@@ -0,0 +1,18 @@
1<div class="card">
2 <div class="card-stacked">
3 <div class="card-content">
4 <span class="card-title dot-ellipsis dot-resize-update">
5 <a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title | raw | striptags }}">
6 {{ entry.title| striptags | truncate(120, true, '…') | raw }}
7 </a>
8 </span>
9 <ul class="tools-list right">
10 <li>
11 <a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}redo{% endif %}</i></a>
12 <a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', { 'id': entry.id }) }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>
13 <a title="{{ 'entry.list.delete'|trans }}" class="tool grey-text delete" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons">delete</i></a>
14 </li>
15 </ul>
16 </div>
17 </div>
18</div>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_reading_time.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_reading_time.html.twig
new file mode 100644
index 00000000..1a932a9f
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_reading_time.html.twig
@@ -0,0 +1,7 @@
1{% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
2<i class="material-icons">timer</i>
3{% if readingTime > 0 %}
4 {{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }}
5{% else %}
6 {{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }}
7{% endif %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
index c610c8d2..5fca53ae 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
@@ -1,17 +1,22 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %} 1{% extends "WallabagCoreBundle::layout.html.twig" %}
2 2
3{% block title %} 3{% block title %}
4 {% set currentTag = '' %} 4 {% set filter = '' %}
5 {% if tag is defined %} 5 {% if tag is defined %}
6 {% set currentTag = tag %} 6 {% set filter = tag %}
7 {% endif %}
8 {% if searchTerm is defined and searchTerm is not empty %}
9 {% set filter = searchTerm %}
7 {% endif %} 10 {% endif %}
8 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %} 11 {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'filter': filter} %}
9{% endblock %} 12{% endblock %}
10 13
11{% block content %} 14{% block content %}
15 {% set listMode = app.user.config.listMode %}
12 <div class="results clearfix"> 16 <div class="results clearfix">
13 <div class="nb-results left"> 17 <div class="nb-results left">
14 {{ 'entry.list.number_on_the_page'|transchoice(entries.count) }} 18 {{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}
19 <a href="{{ path('switch_view_mode') }}"><i class="material-icons">{% if listMode == 0 %}view_list{% else %}view_module{% endif %}</i></a>
15 </div> 20 </div>
16 {% if entries.getNbPages > 1 %} 21 {% if entries.getNbPages > 1 %}
17 {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} 22 {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
@@ -21,8 +26,10 @@
21 <br /> 26 <br />
22 <ul class="row data"> 27 <ul class="row data">
23 {% for entry in entries %} 28 {% for entry in entries %}
24 <li id="entry-{{ entry.id|e }}" class="col l3 m6 s12"> 29 <li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% endif %} s12">
25 {% if entry.previewPicture is null %} 30 {% if listMode == 1 %}
31 {% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %}
32 {% elseif entry.previewPicture is null %}
26 {% include "@WallabagCore/themes/material/Entry/_card_no_preview.html.twig" with {'entry': entry} only %} 33 {% include "@WallabagCore/themes/material/Entry/_card_no_preview.html.twig" with {'entry': entry} only %}
27 {% elseif not entry.previewPicture is null and entry.mimetype starts with 'image/' %} 34 {% elseif not entry.previewPicture is null and entry.mimetype starts with 'image/' %}
28 {% include "@WallabagCore/themes/material/Entry/_card_full_image.html.twig" with {'entry': entry} only %} 35 {% include "@WallabagCore/themes/material/Entry/_card_full_image.html.twig" with {'entry': entry} only %}
@@ -104,6 +111,14 @@
104 </div> 111 </div>
105 112
106 <div class="col s12"> 113 <div class="col s12">
114 {{ form_label(form.httpStatus) }}
115 </div>
116
117 <div class="input-field col s12">
118 {{ form_widget(form.httpStatus) }}
119 </div>
120
121 <div class="col s12">
107 {{ form_label(form.readingTime) }} 122 {{ form_label(form.readingTime) }}
108 </div> 123 </div>
109 <div class="input-field col s6"> 124 <div class="input-field col s6">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
index b445f7d5..15428b92 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
@@ -46,14 +46,14 @@
46 </li> 46 </li>
47 47
48 <li class="bold border-bottom hide-on-med-and-down"> 48 <li class="bold border-bottom hide-on-med-and-down">
49 <a class="waves-effect collapsible-header" href="{{ entry.url|e }}"> 49 <a class="waves-effect collapsible-header original" href="{{ entry.url|e }}" target="_blank">
50 <i class="material-icons small">link</i> 50 <i class="material-icons small">link</i>
51 <span>{{ 'entry.view.left_menu.view_original_article'|trans }}</span> 51 <span>{{ 'entry.view.left_menu.view_original_article'|trans }}</span>
52 </a> 52 </a>
53 <div class="collapsible-body"></div> 53 <div class="collapsible-body"></div>
54 </li> 54 </li>
55 55
56 <li class="bold hide-on-med-and-down"> 56 <li class="bold">
57 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" href="{{ path('reload_entry', { 'id': entry.id }) }}" id="reload"> 57 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" href="{{ path('reload_entry', { 'id': entry.id }) }}" id="reload">
58 <i class="material-icons small">autorenew</i> 58 <i class="material-icons small">autorenew</i>
59 <span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span> 59 <span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span>
@@ -67,7 +67,7 @@
67 {% endif %} 67 {% endif %}
68 68
69 <li class="bold hide-on-med-and-down"> 69 <li class="bold hide-on-med-and-down">
70 <a class="waves-effect collapsible-header" title="{{ markAsReadLabel|trans }}" href="{{ path('archive_entry', { 'id': entry.id }) }}" id="markAsRead"> 70 <a class="waves-effect collapsible-header markasread" title="{{ markAsReadLabel|trans }}" href="{{ path('archive_entry', { 'id': entry.id }) }}" id="markAsRead">
71 <i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}redo{% endif %}</i> 71 <i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}redo{% endif %}</i>
72 <span>{{ markAsReadLabel|trans }}</span> 72 <span>{{ markAsReadLabel|trans }}</span>
73 </a> 73 </a>
@@ -75,21 +75,21 @@
75 </li> 75 </li>
76 76
77 <li class="bold hide-on-med-and-down"> 77 <li class="bold hide-on-med-and-down">
78 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', { 'id': entry.id }) }}" id="setFav"> 78 <a class="waves-effect collapsible-header favorite" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', { 'id': entry.id }) }}" id="setFav">
79 <i class="material-icons spall">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i> 79 <i class="material-icons spall">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
80 <span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span> 80 <span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span>
81 </a> 81 </a>
82 <div class="collapsible-body"></div> 82 <div class="collapsible-body"></div>
83 </li> 83 </li>
84 <li class="bold border-bottom hide-on-med-and-down"> 84 <li class="bold border-bottom">
85 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}"> 85 <a class="waves-effect collapsible-header delete" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}">
86 <i class="material-icons small">delete</i> 86 <i class="material-icons small">delete</i>
87 <span>{{ 'entry.view.left_menu.delete'|trans }}</span> 87 <span>{{ 'entry.view.left_menu.delete'|trans }}</span>
88 </a> 88 </a>
89 <div class="collapsible-body"></div> 89 <div class="collapsible-body"></div>
90 </li> 90 </li>
91 91
92 <li class="bold border-bottom hide-on-med-and-down"> 92 <li class="bold border-bottom">
93 <a class="waves-effect collapsible-header" id="nav-btn-add-tag"> 93 <a class="waves-effect collapsible-header" id="nav-btn-add-tag">
94 <i class="material-icons small">label_outline</i> 94 <i class="material-icons small">label_outline</i>
95 <span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span> 95 <span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span>
@@ -125,7 +125,7 @@
125 {% endif %} 125 {% endif %}
126 {% if craue_setting('share_shaarli') %} 126 {% if craue_setting('share_shaarli') %}
127 <li> 127 <li>
128 <a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank"> 128 <a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}" target="_blank">
129 <i class="tool icon-image icon-image--shaarli" title="shaarli"></i> 129 <i class="tool icon-image icon-image--shaarli" title="shaarli"></i>
130 <span>shaarli</span> 130 <span>shaarli</span>
131 </a> 131 </a>
@@ -139,6 +139,14 @@
139 </a> 139 </a>
140 </li> 140 </li>
141 {% endif %} 141 {% endif %}
142 {% if craue_setting('share_unmark') %}
143 <li>
144 <a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank">
145 <i class="tool icon-image icon-image--unmark" title="unmark"></i>
146 <span>unmark.it</span>
147 </a>
148 </li>
149 {% endif %}
142 {% if craue_setting('carrot') %} 150 {% if craue_setting('carrot') %}
143 <li> 151 <li>
144 <a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" target="_blank" title="carrot"> 152 <a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" target="_blank" title="carrot">
@@ -186,14 +194,6 @@
186 </div> 194 </div>
187 </li> 195 </li>
188 196
189 <li class="bold hide-on-large-only">
190 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}">
191 <i class="material-icons small">delete</i>
192 <span>{{ 'entry.view.left_menu.delete'|trans }}</span>
193 </a>
194 <div class="collapsible-body"></div>
195 </li>
196
197 <li class="bold"> 197 <li class="bold">
198 <a class="waves-effect collapsible-header" href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.description'|trans }}"> 198 <a class="waves-effect collapsible-header" href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.description'|trans }}">
199 <i class="material-icons small">error</i> 199 <i class="material-icons small">error</i>
@@ -214,13 +214,7 @@
214 <aside> 214 <aside>
215 <ul class="tools"> 215 <ul class="tools">
216 <li> 216 <li>
217 {% set readingTime = entry.readingTime / app.user.config.readingSpeed %} 217 {% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %}
218 <i class="material-icons">timer</i>
219 {% if readingTime > 0 %}
220 {{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }}
221 {% else %}
222 {{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }}
223 {% endif %}
224 </li> 218 </li>
225 <li> 219 <li>
226 <i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i> 220 <i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i>
@@ -257,6 +251,17 @@
257 <article> 251 <article>
258 {{ entry.content | raw }} 252 {{ entry.content | raw }}
259 </article> 253 </article>
254
255 <div class="fixed-action-btn horizontal click-to-toggle hide-on-large-only">
256 <a class="btn-floating btn-large">
257 <i class="material-icons">menu</i>
258 </a>
259 <ul>
260 <li><a class="btn-floating" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons">done</i></a></li>
261 <li><a class="btn-floating" href="{{ path('star_entry', { 'id': entry.id }) }}"><i class="material-icons">star_outline</i></a></li>
262 <li><a class="btn-floating" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons">delete</i></a></li>
263 </ul>
264 </div>
260 </div> 265 </div>
261 266
262<script id="annotationroutes" type="application/json"> 267<script id="annotationroutes" type="application/json">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig
new file mode 100644
index 00000000..f25de94d
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/search_form.html.twig
@@ -0,0 +1,15 @@
1<form name="search" method="GET" action="{{ path('search')}}">
2 {% if form_errors(form) %}
3 <span class="black-text">{{ form_errors(form) }}</span>
4 {% endif %}
5
6 {% if form_errors(form.term) %}
7 <span class="black-text">{{ form_errors(form.term) }}</span>
8 {% endif %}
9
10 <input type="hidden" name="currentRoute" value="{{ currentRoute }}" />
11
12 {{ form_widget(form.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'entry.search.placeholder'} }) }}
13
14 {{ form_rest(form) }}
15</form>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
index 6e552560..b702c4b6 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
@@ -9,5 +9,6 @@
9 9
10 {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }} 10 {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }}
11 11
12 {{ form_rest(form) }} 12 {{ form_widget(form.add, {'attr': {'class': 'btn waves-effect waves-light hide-on-large-only'}}) }}
13 {{ form_widget(form._token) }}
13</form> 14</form>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
index f1ef01df..d9fb5d73 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
@@ -19,8 +19,6 @@
19 Materialize.toast('{{ flashMessage|trans }}', 4000); 19 Materialize.toast('{{ flashMessage|trans }}', 4000);
20 </script> 20 </script>
21 {% endfor %} 21 {% endfor %}
22
23 {{ render(controller("WallabagImportBundle:Import:checkQueue")) }}
24{% endblock %} 22{% endblock %}
25 23
26{% block menu %} 24{% block menu %}
@@ -82,37 +80,33 @@
82 {% block title %} 80 {% block title %}
83 {% endblock %} 81 {% endblock %}
84 </div> 82 </div>
85 <div class="input-field nav-panel-buttom"> 83 <ul class="input-field nav-panel-buttom">
86 <ul> 84 <li class="bold">
87 <li class="bold"> 85 <a class="waves-effect tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.add_new_entry'|trans }}" href="{{ path('new') }}" id="nav-btn-add">
88 <a class="waves-effect tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.add_new_entry'|trans }}" href="{{ path('new') }}" id="nav-btn-add"> 86 <i class="material-icons">add</i>
89 <i class="material-icons">add</i> 87 </a>
90 </a> 88 </li>
91 </li> 89 <li>
92 <!--<li> 90 <a class="waves-effect tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.search'|trans }}" href="javascript: void(null);" id="nav-btn-search">
93 <a title="{{ 'menu.top.search'|trans }}" class="waves-effect" href="javascript: void(null);" id="nav-btn-search"> 91 <i class="material-icons">search</i>
94 <i class="material-icons">search</i> 92 </a>
95 </a> 93 </li>
96 </li>--> 94 <li id="button_filters">
97 <li id="button_filters"> 95 <a class="nav-panel-menu button-collapse-right tooltipped js-filters-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters">
98 <a class="nav-panel-menu button-collapse-right tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters"> 96 <i class="material-icons">filter_list</i>
99 <i class="material-icons">filter_list</i> 97 </a>
100 </a> 98 </li>
101 </li> 99 <li id="button_export">
102 <li id="button_export"> 100 <a class="nav-panel-menu button-collapse-right tooltipped js-export-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.export'|trans }}" href="#" data-activates="export">
103 <a class="nav-panel-menu button-collapse-right tooltipped" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.export'|trans }}" href="#" data-activates="export"> 101 <i class="material-icons">file_download</i>
104 <i class="material-icons">file_download</i> 102 </a>
105 </a> 103 </li>
106 </li> 104 </ul>
107 </ul> 105 <div class="input-field nav-panel-search" style="display: none">
106 {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
107 <label for="search" class="active"><i class="material-icons search">search</i></label>
108 <i class="material-icons close">clear</i>
108 </div> 109 </div>
109 <form method="get" action="index.php">
110 <div class="input-field nav-panel-search" style="display: none">
111 <input name="search" id="searchfield" type="search" required placeholder="{{ 'menu.search_form.input_label'|trans }}">
112 <label for="search"><i class="material-icons search">search</i></label>
113 <i class="material-icons close">clear</i>
114 </div>
115 </form>
116 <div class="input-field nav-panel-add" style="display: none"> 110 <div class="input-field nav-panel-add" style="display: none">
117 {{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }} 111 {{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }}
118 <label for="add" class="active"><i class="material-icons add">add</i></label> 112 <label for="add" class="active"><i class="material-icons add">add</i></label>
diff --git a/src/Wallabag/CoreBundle/Tools/Utils.php b/src/Wallabag/CoreBundle/Tools/Utils.php
index 4561f39f..eba21c02 100644
--- a/src/Wallabag/CoreBundle/Tools/Utils.php
+++ b/src/Wallabag/CoreBundle/Tools/Utils.php
@@ -29,6 +29,6 @@ class Utils
29 */ 29 */
30 public static function getReadingTime($text) 30 public static function getReadingTime($text)
31 { 31 {
32 return floor(str_word_count(strip_tags($text)) / 200); 32 return floor(count(preg_split('~[^\p{L}\p{N}\']+~u', strip_tags($text))) / 200);
33 } 33 }
34} 34}
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php
index d1325338..28d01715 100644
--- a/src/Wallabag/ImportBundle/Command/ImportCommand.php
+++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php
@@ -14,10 +14,10 @@ class ImportCommand extends ContainerAwareCommand
14 { 14 {
15 $this 15 $this
16 ->setName('wallabag:import') 16 ->setName('wallabag:import')
17 ->setDescription('Import entries from a JSON export from a wallabag v1 instance') 17 ->setDescription('Import entries from a JSON export')
18 ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate') 18 ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate')
19 ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') 19 ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file')
20 ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: wallabag v1, v2, firefox or chrome', 'v1') 20 ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1')
21 ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false) 21 ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false)
22 ; 22 ;
23 } 23 }
@@ -42,32 +42,36 @@ class ImportCommand extends ContainerAwareCommand
42 42
43 switch ($input->getOption('importer')) { 43 switch ($input->getOption('importer')) {
44 case 'v2': 44 case 'v2':
45 $wallabag = $this->getContainer()->get('wallabag_import.wallabag_v2.import'); 45 $import = $this->getContainer()->get('wallabag_import.wallabag_v2.import');
46 break; 46 break;
47 case 'firefox': 47 case 'firefox':
48 $wallabag = $this->getContainer()->get('wallabag_import.firefox.import'); 48 $import = $this->getContainer()->get('wallabag_import.firefox.import');
49 break; 49 break;
50 case 'chrome': 50 case 'chrome':
51 $wallabag = $this->getContainer()->get('wallabag_import.chrome.import'); 51 $import = $this->getContainer()->get('wallabag_import.chrome.import');
52 break;
53 case 'readability':
54 $import = $this->getContainer()->get('wallabag_import.readability.import');
52 break; 55 break;
53 case 'instapaper': 56 case 'instapaper':
54 $wallabag = $this->getContainer()->get('wallabag_import.instapaper.import'); 57 $import = $this->getContainer()->get('wallabag_import.instapaper.import');
55 break; 58 break;
56 case 'v1': 59 case 'pinboard':
57 default: 60 $import = $this->getContainer()->get('wallabag_import.pinboard.import');
58 $wallabag = $this->getContainer()->get('wallabag_import.wallabag_v1.import');
59 break; 61 break;
62 default:
63 $import = $this->getContainer()->get('wallabag_import.wallabag_v1.import');
60 } 64 }
61 65
62 $wallabag->setMarkAsRead($input->getOption('markAsRead')); 66 $import->setMarkAsRead($input->getOption('markAsRead'));
63 $wallabag->setUser($user); 67 $import->setUser($user);
64 68
65 $res = $wallabag 69 $res = $import
66 ->setFilepath($input->getArgument('filepath')) 70 ->setFilepath($input->getArgument('filepath'))
67 ->import(); 71 ->import();
68 72
69 if (true === $res) { 73 if (true === $res) {
70 $summary = $wallabag->getSummary(); 74 $summary = $import->getSummary();
71 $output->writeln('<info>'.$summary['imported'].' imported</info>'); 75 $output->writeln('<info>'.$summary['imported'].' imported</info>');
72 $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>'); 76 $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>');
73 } 77 }
diff --git a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
index c2c11f11..2d06af44 100644
--- a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
+++ b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
@@ -17,7 +17,7 @@ class RedisWorkerCommand extends ContainerAwareCommand
17 $this 17 $this
18 ->setName('wallabag:import:redis-worker') 18 ->setName('wallabag:import:redis-worker')
19 ->setDescription('Launch Redis worker') 19 ->setDescription('Launch Redis worker')
20 ->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket, readability, firefox, chrome or instapaper') 20 ->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket, readability, pinboard, firefox, chrome or instapaper')
21 ->addOption('maxIterations', '', InputOption::VALUE_OPTIONAL, 'Number of iterations before stoping', false) 21 ->addOption('maxIterations', '', InputOption::VALUE_OPTIONAL, 'Number of iterations before stoping', false)
22 ; 22 ;
23 } 23 }
@@ -36,7 +36,7 @@ class RedisWorkerCommand extends ContainerAwareCommand
36 $worker = new QueueWorker( 36 $worker = new QueueWorker(
37 $this->getContainer()->get('wallabag_import.queue.redis.'.$serviceName), 37 $this->getContainer()->get('wallabag_import.queue.redis.'.$serviceName),
38 $this->getContainer()->get('wallabag_import.consumer.redis.'.$serviceName), 38 $this->getContainer()->get('wallabag_import.consumer.redis.'.$serviceName),
39 $input->getOption('maxIterations') 39 (int) $input->getOption('maxIterations')
40 ); 40 );
41 41
42 $worker->start(); 42 $worker->start();
diff --git a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
index b893ea29..fc175f67 100644
--- a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
+++ b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
@@ -9,19 +9,23 @@ use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Entity\Tag; 9use Wallabag\CoreBundle\Entity\Tag;
10use Psr\Log\LoggerInterface; 10use Psr\Log\LoggerInterface;
11use Psr\Log\NullLogger; 11use Psr\Log\NullLogger;
12use Symfony\Component\EventDispatcher\EventDispatcherInterface;
13use Wallabag\CoreBundle\Event\EntrySavedEvent;
12 14
13abstract class AbstractConsumer 15abstract class AbstractConsumer
14{ 16{
15 protected $em; 17 protected $em;
16 protected $userRepository; 18 protected $userRepository;
17 protected $import; 19 protected $import;
20 protected $eventDispatcher;
18 protected $logger; 21 protected $logger;
19 22
20 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, LoggerInterface $logger = null) 23 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null)
21 { 24 {
22 $this->em = $em; 25 $this->em = $em;
23 $this->userRepository = $userRepository; 26 $this->userRepository = $userRepository;
24 $this->import = $import; 27 $this->import = $import;
28 $this->eventDispatcher = $eventDispatcher;
25 $this->logger = $logger ?: new NullLogger(); 29 $this->logger = $logger ?: new NullLogger();
26 } 30 }
27 31
@@ -59,6 +63,9 @@ abstract class AbstractConsumer
59 try { 63 try {
60 $this->em->flush(); 64 $this->em->flush();
61 65
66 // entry saved, dispatch event about it!
67 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
68
62 // clear only affected entities 69 // clear only affected entities
63 $this->em->clear(Entry::class); 70 $this->em->clear(Entry::class);
64 $this->em->clear(Tag::class); 71 $this->em->clear(Tag::class);
diff --git a/src/Wallabag/ImportBundle/Controller/BrowserController.php b/src/Wallabag/ImportBundle/Controller/BrowserController.php
index 144a4880..e119098f 100644
--- a/src/Wallabag/ImportBundle/Controller/BrowserController.php
+++ b/src/Wallabag/ImportBundle/Controller/BrowserController.php
@@ -39,7 +39,7 @@ abstract class BrowserController extends Controller
39 $wallabag = $this->getImportService(); 39 $wallabag = $this->getImportService();
40 $wallabag->setUser($this->getUser()); 40 $wallabag->setUser($this->getUser());
41 41
42 if ($form->isValid()) { 42 if ($form->isSubmitted() && $form->isValid()) {
43 $file = $form->get('file')->getData(); 43 $file = $form->get('file')->getData();
44 $markAsRead = $form->get('mark_as_read')->getData(); 44 $markAsRead = $form->get('mark_as_read')->getData();
45 $name = $this->getUser()->getId().'.json'; 45 $name = $this->getUser()->getId().'.json';
diff --git a/src/Wallabag/ImportBundle/Controller/ImportController.php b/src/Wallabag/ImportBundle/Controller/ImportController.php
index 15de75ff..237c748e 100644
--- a/src/Wallabag/ImportBundle/Controller/ImportController.php
+++ b/src/Wallabag/ImportBundle/Controller/ImportController.php
@@ -42,6 +42,7 @@ class ImportController extends Controller
42 + $this->getTotalMessageInRabbitQueue('firefox') 42 + $this->getTotalMessageInRabbitQueue('firefox')
43 + $this->getTotalMessageInRabbitQueue('chrome') 43 + $this->getTotalMessageInRabbitQueue('chrome')
44 + $this->getTotalMessageInRabbitQueue('instapaper') 44 + $this->getTotalMessageInRabbitQueue('instapaper')
45 + $this->getTotalMessageInRabbitQueue('pinboard')
45 ; 46 ;
46 } catch (\Exception $e) { 47 } catch (\Exception $e) {
47 $rabbitNotInstalled = true; 48 $rabbitNotInstalled = true;
@@ -57,6 +58,7 @@ class ImportController extends Controller
57 + $redis->llen('wallabag.import.firefox') 58 + $redis->llen('wallabag.import.firefox')
58 + $redis->llen('wallabag.import.chrome') 59 + $redis->llen('wallabag.import.chrome')
59 + $redis->llen('wallabag.import.instapaper') 60 + $redis->llen('wallabag.import.instapaper')
61 + $redis->llen('wallabag.import.pinboard')
60 ; 62 ;
61 } catch (\Exception $e) { 63 } catch (\Exception $e) {
62 $redisNotInstalled = true; 64 $redisNotInstalled = true;
diff --git a/src/Wallabag/ImportBundle/Controller/InstapaperController.php b/src/Wallabag/ImportBundle/Controller/InstapaperController.php
index c3fc8a39..0251acb9 100644
--- a/src/Wallabag/ImportBundle/Controller/InstapaperController.php
+++ b/src/Wallabag/ImportBundle/Controller/InstapaperController.php
@@ -26,7 +26,7 @@ class InstapaperController extends Controller
26 $instapaper->setProducer($this->get('wallabag_import.producer.redis.instapaper')); 26 $instapaper->setProducer($this->get('wallabag_import.producer.redis.instapaper'));
27 } 27 }
28 28
29 if ($form->isValid()) { 29 if ($form->isSubmitted() && $form->isValid()) {
30 $file = $form->get('file')->getData(); 30 $file = $form->get('file')->getData();
31 $markAsRead = $form->get('mark_as_read')->getData(); 31 $markAsRead = $form->get('mark_as_read')->getData();
32 $name = 'instapaper_'.$this->getUser()->getId().'.csv'; 32 $name = 'instapaper_'.$this->getUser()->getId().'.csv';
diff --git a/src/Wallabag/ImportBundle/Controller/PinboardController.php b/src/Wallabag/ImportBundle/Controller/PinboardController.php
new file mode 100644
index 00000000..d0ad8aa8
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Controller/PinboardController.php
@@ -0,0 +1,77 @@
1<?php
2
3namespace Wallabag\ImportBundle\Controller;
4
5use Symfony\Bundle\FrameworkBundle\Controller\Controller;
6use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
7use Symfony\Component\HttpFoundation\Request;
8use Wallabag\ImportBundle\Form\Type\UploadImportType;
9
10class PinboardController extends Controller
11{
12 /**
13 * @Route("/pinboard", name="import_pinboard")
14 */
15 public function indexAction(Request $request)
16 {
17 $form = $this->createForm(UploadImportType::class);
18 $form->handleRequest($request);
19
20 $pinboard = $this->get('wallabag_import.pinboard.import');
21 $pinboard->setUser($this->getUser());
22
23 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
24 $pinboard->setProducer($this->get('old_sound_rabbit_mq.import_pinboard_producer'));
25 } elseif ($this->get('craue_config')->get('import_with_redis')) {
26 $pinboard->setProducer($this->get('wallabag_import.producer.redis.pinboard'));
27 }
28
29 if ($form->isSubmitted() && $form->isValid()) {
30 $file = $form->get('file')->getData();
31 $markAsRead = $form->get('mark_as_read')->getData();
32 $name = 'pinboard_'.$this->getUser()->getId().'.json';
33
34 if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
35 $res = $pinboard
36 ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
37 ->setMarkAsRead($markAsRead)
38 ->import();
39
40 $message = 'flashes.import.notice.failed';
41
42 if (true === $res) {
43 $summary = $pinboard->getSummary();
44 $message = $this->get('translator')->trans('flashes.import.notice.summary', [
45 '%imported%' => $summary['imported'],
46 '%skipped%' => $summary['skipped'],
47 ]);
48
49 if (0 < $summary['queued']) {
50 $message = $this->get('translator')->trans('flashes.import.notice.summary_with_queue', [
51 '%queued%' => $summary['queued'],
52 ]);
53 }
54
55 unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
56 }
57
58 $this->get('session')->getFlashBag()->add(
59 'notice',
60 $message
61 );
62
63 return $this->redirect($this->generateUrl('homepage'));
64 } else {
65 $this->get('session')->getFlashBag()->add(
66 'notice',
67 'flashes.import.notice.failed_on_file'
68 );
69 }
70 }
71
72 return $this->render('WallabagImportBundle:Pinboard:index.html.twig', [
73 'form' => $form->createView(),
74 'import' => $pinboard,
75 ]);
76 }
77}
diff --git a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
index d00e22c2..aa732ddd 100644
--- a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
+++ b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
@@ -26,7 +26,7 @@ class ReadabilityController extends Controller
26 $readability->setProducer($this->get('wallabag_import.producer.redis.readability')); 26 $readability->setProducer($this->get('wallabag_import.producer.redis.readability'));
27 } 27 }
28 28
29 if ($form->isValid()) { 29 if ($form->isSubmitted() && $form->isValid()) {
30 $file = $form->get('file')->getData(); 30 $file = $form->get('file')->getData();
31 $markAsRead = $form->get('mark_as_read')->getData(); 31 $markAsRead = $form->get('mark_as_read')->getData();
32 $name = 'readability_'.$this->getUser()->getId().'.json'; 32 $name = 'readability_'.$this->getUser()->getId().'.json';
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagController.php b/src/Wallabag/ImportBundle/Controller/WallabagController.php
index 9c0cde80..e81c1ca9 100644
--- a/src/Wallabag/ImportBundle/Controller/WallabagController.php
+++ b/src/Wallabag/ImportBundle/Controller/WallabagController.php
@@ -40,7 +40,7 @@ abstract class WallabagController extends Controller
40 $wallabag = $this->getImportService(); 40 $wallabag = $this->getImportService();
41 $wallabag->setUser($this->getUser()); 41 $wallabag->setUser($this->getUser());
42 42
43 if ($form->isValid()) { 43 if ($form->isSubmitted() && $form->isValid()) {
44 $file = $form->get('file')->getData(); 44 $file = $form->get('file')->getData();
45 $markAsRead = $form->get('mark_as_read')->getData(); 45 $markAsRead = $form->get('mark_as_read')->getData();
46 $name = $this->getUser()->getId().'.json'; 46 $name = $this->getUser()->getId().'.json';
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/ChromeImport.php b/src/Wallabag/ImportBundle/Import/ChromeImport.php
index d7620bcb..2667890f 100644
--- a/src/Wallabag/ImportBundle/Import/ChromeImport.php
+++ b/src/Wallabag/ImportBundle/Import/ChromeImport.php
@@ -37,9 +37,10 @@ class ChromeImport extends BrowserImport
37 { 37 {
38 $data = [ 38 $data = [
39 'title' => $entry['name'], 39 'title' => $entry['name'],
40 'html' => '', 40 'html' => false,
41 'url' => $entry['url'], 41 'url' => $entry['url'],
42 'is_archived' => $this->markAsRead, 42 'is_archived' => (int) $this->markAsRead,
43 'is_starred' => false,
43 'tags' => '', 44 'tags' => '',
44 'created_at' => substr($entry['date_added'], 0, 10), 45 'created_at' => substr($entry['date_added'], 0, 10),
45 ]; 46 ];
diff --git a/src/Wallabag/ImportBundle/Import/FirefoxImport.php b/src/Wallabag/ImportBundle/Import/FirefoxImport.php
index e010f5a4..c50c69b3 100644
--- a/src/Wallabag/ImportBundle/Import/FirefoxImport.php
+++ b/src/Wallabag/ImportBundle/Import/FirefoxImport.php
@@ -37,9 +37,10 @@ class FirefoxImport extends BrowserImport
37 { 37 {
38 $data = [ 38 $data = [
39 'title' => $entry['title'], 39 'title' => $entry['title'],
40 'html' => '', 40 'html' => false,
41 'url' => $entry['uri'], 41 'url' => $entry['uri'],
42 'is_archived' => $this->markAsRead, 42 'is_archived' => (int) $this->markAsRead,
43 'is_starred' => false,
43 'tags' => '', 44 'tags' => '',
44 'created_at' => substr($entry['dateAdded'], 0, 10), 45 'created_at' => substr($entry['dateAdded'], 0, 10),
45 ]; 46 ];
diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
index cf4c785c..70a53f1a 100644
--- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php
+++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
@@ -74,8 +74,7 @@ class InstapaperImport extends AbstractImport
74 'status' => $data[3], 74 'status' => $data[3],
75 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred', 75 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred',
76 'is_starred' => $data[3] === 'Starred', 76 'is_starred' => $data[3] === 'Starred',
77 'content_type' => '', 77 'html' => false,
78 'language' => '',
79 ]; 78 ];
80 } 79 }
81 fclose($handle); 80 fclose($handle);
diff --git a/src/Wallabag/ImportBundle/Import/PinboardImport.php b/src/Wallabag/ImportBundle/Import/PinboardImport.php
new file mode 100644
index 00000000..d9865534
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Import/PinboardImport.php
@@ -0,0 +1,141 @@
1<?php
2
3namespace Wallabag\ImportBundle\Import;
4
5use Wallabag\CoreBundle\Entity\Entry;
6
7class PinboardImport extends AbstractImport
8{
9 private $filepath;
10
11 /**
12 * {@inheritdoc}
13 */
14 public function getName()
15 {
16 return 'Pinboard';
17 }
18
19 /**
20 * {@inheritdoc}
21 */
22 public function getUrl()
23 {
24 return 'import_pinboard';
25 }
26
27 /**
28 * {@inheritdoc}
29 */
30 public function getDescription()
31 {
32 return 'import.pinboard.description';
33 }
34
35 /**
36 * Set file path to the json file.
37 *
38 * @param string $filepath
39 */
40 public function setFilepath($filepath)
41 {
42 $this->filepath = $filepath;
43
44 return $this;
45 }
46
47 /**
48 * {@inheritdoc}
49 */
50 public function import()
51 {
52 if (!$this->user) {
53 $this->logger->error('PinboardImport: user is not defined');
54
55 return false;
56 }
57
58 if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
59 $this->logger->error('PinboardImport: unable to read file', ['filepath' => $this->filepath]);
60
61 return false;
62 }
63
64 $data = json_decode(file_get_contents($this->filepath), true);
65
66 if (empty($data)) {
67 $this->logger->error('PinboardImport: no entries in imported file');
68
69 return false;
70 }
71
72 if ($this->producer) {
73 $this->parseEntriesForProducer($data);
74
75 return true;
76 }
77
78 $this->parseEntries($data);
79
80 return true;
81 }
82
83 /**
84 * {@inheritdoc}
85 */
86 public function parseEntry(array $importedEntry)
87 {
88 $existingEntry = $this->em
89 ->getRepository('WallabagCoreBundle:Entry')
90 ->findByUrlAndUserId($importedEntry['href'], $this->user->getId());
91
92 if (false !== $existingEntry) {
93 ++$this->skippedEntries;
94
95 return;
96 }
97
98 $data = [
99 'title' => $importedEntry['description'],
100 'url' => $importedEntry['href'],
101 'is_archived' => ('no' === $importedEntry['toread']) || $this->markAsRead,
102 'is_starred' => false,
103 'created_at' => $importedEntry['time'],
104 'tags' => explode(' ', $importedEntry['tags']),
105 ];
106
107 $entry = new Entry($this->user);
108 $entry->setUrl($data['url']);
109 $entry->setTitle($data['title']);
110
111 // update entry with content (in case fetching failed, the given entry will be return)
112 $entry = $this->fetchContent($entry, $data['url'], $data);
113
114 if (!empty($data['tags'])) {
115 $this->contentProxy->assignTagsToEntry(
116 $entry,
117 $data['tags'],
118 $this->em->getUnitOfWork()->getScheduledEntityInsertions()
119 );
120 }
121
122 $entry->setArchived($data['is_archived']);
123 $entry->setStarred($data['is_starred']);
124 $entry->setCreatedAt(new \DateTime($data['created_at']));
125
126 $this->em->persist($entry);
127 ++$this->importedEntries;
128
129 return $entry;
130 }
131
132 /**
133 * {@inheritdoc}
134 */
135 protected function setEntryAsRead(array $importedEntry)
136 {
137 $importedEntry['toread'] = 'no';
138
139 return $importedEntry;
140 }
141}
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/Import/ReadabilityImport.php b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
index b8c0f777..de320d23 100644
--- a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
+++ b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
@@ -98,11 +98,10 @@ class ReadabilityImport extends AbstractImport
98 $data = [ 98 $data = [
99 'title' => $importedEntry['article__title'], 99 'title' => $importedEntry['article__title'],
100 'url' => $importedEntry['article__url'], 100 'url' => $importedEntry['article__url'],
101 'content_type' => '',
102 'language' => '',
103 'is_archived' => $importedEntry['archive'] || $this->markAsRead, 101 'is_archived' => $importedEntry['archive'] || $this->markAsRead,
104 'is_starred' => $importedEntry['favorite'], 102 'is_starred' => $importedEntry['favorite'],
105 'created_at' => $importedEntry['date_added'], 103 'created_at' => $importedEntry['date_added'],
104 'html' => false,
106 ]; 105 ];
107 106
108 $entry = new Entry($this->user); 107 $entry = new Entry($this->user);
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
index 4f001062..59e3ce02 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
@@ -37,8 +37,6 @@ class WallabagV1Import extends WallabagImport
37 'title' => $entry['title'], 37 'title' => $entry['title'],
38 'html' => $entry['content'], 38 'html' => $entry['content'],
39 'url' => $entry['url'], 39 'url' => $entry['url'],
40 'content_type' => '',
41 'language' => '',
42 'is_archived' => $entry['is_read'] || $this->markAsRead, 40 'is_archived' => $entry['is_read'] || $this->markAsRead,
43 'is_starred' => $entry['is_fav'], 41 'is_starred' => $entry['is_fav'],
44 'tags' => '', 42 'tags' => '',
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
index 37c8ca14..d2a89d79 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
@@ -36,7 +36,8 @@ class WallabagV2Import extends WallabagImport
36 return [ 36 return [
37 'html' => $entry['content'], 37 'html' => $entry['content'],
38 'content_type' => $entry['mimetype'], 38 'content_type' => $entry['mimetype'],
39 'is_archived' => ($entry['is_archived'] || $this->markAsRead), 39 'is_archived' => (int) ($entry['is_archived'] || $this->markAsRead),
40 'is_starred' => false,
40 ] + $entry; 41 ] + $entry;
41 } 42 }
42 43
diff --git a/src/Wallabag/ImportBundle/Resources/config/rabbit.yml b/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
index 70b8a0d4..e9ecb846 100644
--- a/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
@@ -6,6 +6,7 @@ services:
6 - "@doctrine.orm.entity_manager" 6 - "@doctrine.orm.entity_manager"
7 - "@wallabag_user.user_repository" 7 - "@wallabag_user.user_repository"
8 - "@wallabag_import.pocket.import" 8 - "@wallabag_import.pocket.import"
9 - "@event_dispatcher"
9 - "@logger" 10 - "@logger"
10 wallabag_import.consumer.amqp.readability: 11 wallabag_import.consumer.amqp.readability:
11 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 12 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -13,6 +14,7 @@ services:
13 - "@doctrine.orm.entity_manager" 14 - "@doctrine.orm.entity_manager"
14 - "@wallabag_user.user_repository" 15 - "@wallabag_user.user_repository"
15 - "@wallabag_import.readability.import" 16 - "@wallabag_import.readability.import"
17 - "@event_dispatcher"
16 - "@logger" 18 - "@logger"
17 wallabag_import.consumer.amqp.instapaper: 19 wallabag_import.consumer.amqp.instapaper:
18 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 20 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -20,6 +22,15 @@ services:
20 - "@doctrine.orm.entity_manager" 22 - "@doctrine.orm.entity_manager"
21 - "@wallabag_user.user_repository" 23 - "@wallabag_user.user_repository"
22 - "@wallabag_import.instapaper.import" 24 - "@wallabag_import.instapaper.import"
25 - "@event_dispatcher"
26 - "@logger"
27 wallabag_import.consumer.amqp.pinboard:
28 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
29 arguments:
30 - "@doctrine.orm.entity_manager"
31 - "@wallabag_user.user_repository"
32 - "@wallabag_import.pinboard.import"
33 - "@event_dispatcher"
23 - "@logger" 34 - "@logger"
24 wallabag_import.consumer.amqp.wallabag_v1: 35 wallabag_import.consumer.amqp.wallabag_v1:
25 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 36 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -27,6 +38,7 @@ services:
27 - "@doctrine.orm.entity_manager" 38 - "@doctrine.orm.entity_manager"
28 - "@wallabag_user.user_repository" 39 - "@wallabag_user.user_repository"
29 - "@wallabag_import.wallabag_v1.import" 40 - "@wallabag_import.wallabag_v1.import"
41 - "@event_dispatcher"
30 - "@logger" 42 - "@logger"
31 wallabag_import.consumer.amqp.wallabag_v2: 43 wallabag_import.consumer.amqp.wallabag_v2:
32 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 44 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -34,6 +46,7 @@ services:
34 - "@doctrine.orm.entity_manager" 46 - "@doctrine.orm.entity_manager"
35 - "@wallabag_user.user_repository" 47 - "@wallabag_user.user_repository"
36 - "@wallabag_import.wallabag_v2.import" 48 - "@wallabag_import.wallabag_v2.import"
49 - "@event_dispatcher"
37 - "@logger" 50 - "@logger"
38 wallabag_import.consumer.amqp.firefox: 51 wallabag_import.consumer.amqp.firefox:
39 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 52 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -41,6 +54,7 @@ services:
41 - "@doctrine.orm.entity_manager" 54 - "@doctrine.orm.entity_manager"
42 - "@wallabag_user.user_repository" 55 - "@wallabag_user.user_repository"
43 - "@wallabag_import.firefox.import" 56 - "@wallabag_import.firefox.import"
57 - "@event_dispatcher"
44 - "@logger" 58 - "@logger"
45 wallabag_import.consumer.amqp.chrome: 59 wallabag_import.consumer.amqp.chrome:
46 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 60 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -48,4 +62,5 @@ services:
48 - "@doctrine.orm.entity_manager" 62 - "@doctrine.orm.entity_manager"
49 - "@wallabag_user.user_repository" 63 - "@wallabag_user.user_repository"
50 - "@wallabag_import.chrome.import" 64 - "@wallabag_import.chrome.import"
65 - "@event_dispatcher"
51 - "@logger" 66 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/redis.yml b/src/Wallabag/ImportBundle/Resources/config/redis.yml
index 0a81e1b5..091cdba0 100644
--- a/src/Wallabag/ImportBundle/Resources/config/redis.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/redis.yml
@@ -18,6 +18,7 @@ services:
18 - "@doctrine.orm.entity_manager" 18 - "@doctrine.orm.entity_manager"
19 - "@wallabag_user.user_repository" 19 - "@wallabag_user.user_repository"
20 - "@wallabag_import.readability.import" 20 - "@wallabag_import.readability.import"
21 - "@event_dispatcher"
21 - "@logger" 22 - "@logger"
22 23
23 # instapaper 24 # instapaper
@@ -38,6 +39,28 @@ services:
38 - "@doctrine.orm.entity_manager" 39 - "@doctrine.orm.entity_manager"
39 - "@wallabag_user.user_repository" 40 - "@wallabag_user.user_repository"
40 - "@wallabag_import.instapaper.import" 41 - "@wallabag_import.instapaper.import"
42 - "@event_dispatcher"
43 - "@logger"
44
45 # pinboard
46 wallabag_import.queue.redis.pinboard:
47 class: Simpleue\Queue\RedisQueue
48 arguments:
49 - "@wallabag_core.redis.client"
50 - "wallabag.import.pinboard"
51
52 wallabag_import.producer.redis.pinboard:
53 class: Wallabag\ImportBundle\Redis\Producer
54 arguments:
55 - "@wallabag_import.queue.redis.pinboard"
56
57 wallabag_import.consumer.redis.pinboard:
58 class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
59 arguments:
60 - "@doctrine.orm.entity_manager"
61 - "@wallabag_user.user_repository"
62 - "@wallabag_import.pinboard.import"
63 - "@event_dispatcher"
41 - "@logger" 64 - "@logger"
42 65
43 # pocket 66 # pocket
@@ -58,6 +81,7 @@ services:
58 - "@doctrine.orm.entity_manager" 81 - "@doctrine.orm.entity_manager"
59 - "@wallabag_user.user_repository" 82 - "@wallabag_user.user_repository"
60 - "@wallabag_import.pocket.import" 83 - "@wallabag_import.pocket.import"
84 - "@event_dispatcher"
61 - "@logger" 85 - "@logger"
62 86
63 # wallabag v1 87 # wallabag v1
@@ -78,6 +102,7 @@ services:
78 - "@doctrine.orm.entity_manager" 102 - "@doctrine.orm.entity_manager"
79 - "@wallabag_user.user_repository" 103 - "@wallabag_user.user_repository"
80 - "@wallabag_import.wallabag_v1.import" 104 - "@wallabag_import.wallabag_v1.import"
105 - "@event_dispatcher"
81 - "@logger" 106 - "@logger"
82 107
83 # wallabag v2 108 # wallabag v2
@@ -98,6 +123,7 @@ services:
98 - "@doctrine.orm.entity_manager" 123 - "@doctrine.orm.entity_manager"
99 - "@wallabag_user.user_repository" 124 - "@wallabag_user.user_repository"
100 - "@wallabag_import.wallabag_v2.import" 125 - "@wallabag_import.wallabag_v2.import"
126 - "@event_dispatcher"
101 - "@logger" 127 - "@logger"
102 128
103 # firefox 129 # firefox
@@ -118,6 +144,7 @@ services:
118 - "@doctrine.orm.entity_manager" 144 - "@doctrine.orm.entity_manager"
119 - "@wallabag_user.user_repository" 145 - "@wallabag_user.user_repository"
120 - "@wallabag_import.firefox.import" 146 - "@wallabag_import.firefox.import"
147 - "@event_dispatcher"
121 - "@logger" 148 - "@logger"
122 149
123 # chrome 150 # chrome
@@ -138,4 +165,5 @@ services:
138 - "@doctrine.orm.entity_manager" 165 - "@doctrine.orm.entity_manager"
139 - "@wallabag_user.user_repository" 166 - "@wallabag_user.user_repository"
140 - "@wallabag_import.chrome.import" 167 - "@wallabag_import.chrome.import"
168 - "@event_dispatcher"
141 - "@logger" 169 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml
index d600be0f..c4fe3f92 100644
--- a/src/Wallabag/ImportBundle/Resources/config/services.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/services.yml
@@ -20,6 +20,7 @@ services:
20 arguments: 20 arguments:
21 - "@doctrine.orm.entity_manager" 21 - "@doctrine.orm.entity_manager"
22 - "@wallabag_core.content_proxy" 22 - "@wallabag_core.content_proxy"
23 - "@event_dispatcher"
23 calls: 24 calls:
24 - [ setClient, [ "@wallabag_import.pocket.client" ] ] 25 - [ setClient, [ "@wallabag_import.pocket.client" ] ]
25 - [ setLogger, [ "@logger" ]] 26 - [ setLogger, [ "@logger" ]]
@@ -31,6 +32,7 @@ services:
31 arguments: 32 arguments:
32 - "@doctrine.orm.entity_manager" 33 - "@doctrine.orm.entity_manager"
33 - "@wallabag_core.content_proxy" 34 - "@wallabag_core.content_proxy"
35 - "@event_dispatcher"
34 calls: 36 calls:
35 - [ setLogger, [ "@logger" ]] 37 - [ setLogger, [ "@logger" ]]
36 tags: 38 tags:
@@ -41,6 +43,7 @@ services:
41 arguments: 43 arguments:
42 - "@doctrine.orm.entity_manager" 44 - "@doctrine.orm.entity_manager"
43 - "@wallabag_core.content_proxy" 45 - "@wallabag_core.content_proxy"
46 - "@event_dispatcher"
44 calls: 47 calls:
45 - [ setLogger, [ "@logger" ]] 48 - [ setLogger, [ "@logger" ]]
46 tags: 49 tags:
@@ -51,6 +54,7 @@ services:
51 arguments: 54 arguments:
52 - "@doctrine.orm.entity_manager" 55 - "@doctrine.orm.entity_manager"
53 - "@wallabag_core.content_proxy" 56 - "@wallabag_core.content_proxy"
57 - "@event_dispatcher"
54 calls: 58 calls:
55 - [ setLogger, [ "@logger" ]] 59 - [ setLogger, [ "@logger" ]]
56 tags: 60 tags:
@@ -61,16 +65,29 @@ services:
61 arguments: 65 arguments:
62 - "@doctrine.orm.entity_manager" 66 - "@doctrine.orm.entity_manager"
63 - "@wallabag_core.content_proxy" 67 - "@wallabag_core.content_proxy"
68 - "@event_dispatcher"
64 calls: 69 calls:
65 - [ setLogger, [ "@logger" ]] 70 - [ setLogger, [ "@logger" ]]
66 tags: 71 tags:
67 - { name: wallabag_import.import, alias: instapaper } 72 - { name: wallabag_import.import, alias: instapaper }
68 73
74 wallabag_import.pinboard.import:
75 class: Wallabag\ImportBundle\Import\PinboardImport
76 arguments:
77 - "@doctrine.orm.entity_manager"
78 - "@wallabag_core.content_proxy"
79 - "@event_dispatcher"
80 calls:
81 - [ setLogger, [ "@logger" ]]
82 tags:
83 - { name: wallabag_import.import, alias: pinboard }
84
69 wallabag_import.firefox.import: 85 wallabag_import.firefox.import:
70 class: Wallabag\ImportBundle\Import\FirefoxImport 86 class: Wallabag\ImportBundle\Import\FirefoxImport
71 arguments: 87 arguments:
72 - "@doctrine.orm.entity_manager" 88 - "@doctrine.orm.entity_manager"
73 - "@wallabag_core.content_proxy" 89 - "@wallabag_core.content_proxy"
90 - "@event_dispatcher"
74 calls: 91 calls:
75 - [ setLogger, [ "@logger" ]] 92 - [ setLogger, [ "@logger" ]]
76 tags: 93 tags:
@@ -80,6 +97,7 @@ services:
80 arguments: 97 arguments:
81 - "@doctrine.orm.entity_manager" 98 - "@doctrine.orm.entity_manager"
82 - "@wallabag_core.content_proxy" 99 - "@wallabag_core.content_proxy"
100 - "@event_dispatcher"
83 calls: 101 calls:
84 - [ setLogger, [ "@logger" ]] 102 - [ setLogger, [ "@logger" ]]
85 tags: 103 tags:
diff --git a/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig
index ead828c6..93b08540 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
9 <div class="row"> 11 <div class="row">
10 <blockquote>{{ import.description|trans|raw }}</blockquote> 12 <blockquote>{{ import.description|trans|raw }}</blockquote>
11 <p>{{ 'import.chrome.how_to'|trans }}</p> 13 <p>{{ 'import.chrome.how_to'|trans }}</p>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig
index f975da3f..ced3f008 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
9 <div class="row"> 11 <div class="row">
10 <blockquote>{{ import.description|trans|raw }}</blockquote> 12 <blockquote>{{ import.description|trans|raw }}</blockquote>
11 <p>{{ 'import.firefox.how_to'|trans }}</p> 13 <p>{{ 'import.firefox.how_to'|trans }}</p>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig b/src/Wallabag/ImportBundle/Resources/views/Import/_information.html.twig
index 2390a41f..48bbcfe7 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Import/_information.html.twig
@@ -1,8 +1,15 @@
1{% set redis = craue_setting('import_with_redis') %} 1{% set redis = craue_setting('import_with_redis') %}
2{% set rabbit = craue_setting('import_with_rabbitmq') %} 2{% set rabbit = craue_setting('import_with_rabbitmq') %}
3{% set downloadImages = craue_setting('download_images_enabled') %}
3 4
4{% if redis or rabbit %} 5{% if redis or rabbit %}
5 <div class="card-panel yellow darken-1 black-text"> 6 <div class="card-panel yellow darken-1 black-text">
6 {{ 'import.worker.enabled'|trans }} <strong>{% if rabbit %}RabbitMQ{% elseif redis %}Redis{% endif %}</strong> 7 {{ 'import.worker.enabled'|trans }} <strong>{% if rabbit %}RabbitMQ{% elseif redis %}Redis{% endif %}</strong>
7 </div> 8 </div>
8{% endif %} 9{% endif %}
10
11{% if not redis and not rabbit and downloadImages %}
12 <div class="card-panel orange darken-1 black-text">
13 {{ 'import.worker.download_images_warning'|trans|raw }}
14 </div>
15{% endif %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig
index 6ea5e0f4..b79a1470 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig
@@ -2,10 +2,18 @@
2 2
3{% block title %}{{ 'import.page_title'|trans }}{% endblock %} 3{% block title %}{{ 'import.page_title'|trans }}{% endblock %}
4 4
5{% block messages %}
6 {{ render(controller("WallabagImportBundle:Import:checkQueue")) }}
7
8 {{ parent() }}
9{% endblock %}
10
5{% block content %} 11{% block content %}
6<div class="row"> 12<div class="row">
7 <div class="col s12"> 13 <div class="col s12">
8 <div class="card-panel settings"> 14 <div class="card-panel settings">
15 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
16
9 {{ 'import.page_description'|trans }} 17 {{ 'import.page_description'|trans }}
10 <ul> 18 <ul>
11 {% for import in imports %} 19 {% for import in imports %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig
index 5789361f..28165d19 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 <div class="row"> 11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig
new file mode 100644
index 00000000..43f196ad
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig
@@ -0,0 +1,45 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %}
2
3{% block title %}{{ 'import.pinboard.page_title'|trans }}{% endblock %}
4
5{% block content %}
6<div class="row">
7 <div class="col s12">
8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote>
13 <p>{{ 'import.pinboard.how_to'|trans }}</p>
14
15 <div class="col s12">
16 {{ form_start(form, {'method': 'POST'}) }}
17 {{ form_errors(form) }}
18 <div class="row">
19 <div class="file-field input-field col s12">
20 {{ form_errors(form.file) }}
21 <div class="btn">
22 <span>{{ form.file.vars.label|trans }}</span>
23 {{ form_widget(form.file) }}
24 </div>
25 <div class="file-path-wrapper">
26 <input class="file-path validate" type="text">
27 </div>
28 </div>
29 <div class="input-field col s6 with-checkbox">
30 <h6>{{ 'import.form.mark_as_read_title'|trans }}</h6>
31 {{ form_widget(form.mark_as_read) }}
32 {{ form_label(form.mark_as_read) }}
33 </div>
34 </div>
35
36 {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'} }) }}
37
38 {{ form_rest(form) }}
39 </form>
40 </div>
41 </div>
42 </div>
43 </div>
44</div>
45{% endblock %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
index 6195fa07..536e3d1a 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 {% if not has_consumer_key %} 11 {% if not has_consumer_key %}
12 <div class="card-panel red white-text"> 12 <div class="card-panel red white-text">
diff --git a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
index 74653b0f..737b0adf 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 <div class="row"> 11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
diff --git a/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
index 0b19bc34..974b2c73 100644
--- a/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 <div class="row"> 11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php
index d98ae76a..3a167de7 100644
--- a/src/Wallabag/UserBundle/Entity/User.php
+++ b/src/Wallabag/UserBundle/Entity/User.php
@@ -11,6 +11,7 @@ use JMS\Serializer\Annotation\ExclusionPolicy;
11use JMS\Serializer\Annotation\Expose; 11use JMS\Serializer\Annotation\Expose;
12use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; 12use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
13use Symfony\Component\Security\Core\User\UserInterface; 13use Symfony\Component\Security\Core\User\UserInterface;
14use Wallabag\ApiBundle\Entity\Client;
14use Wallabag\CoreBundle\Entity\Config; 15use Wallabag\CoreBundle\Entity\Config;
15use Wallabag\CoreBundle\Entity\Entry; 16use Wallabag\CoreBundle\Entity\Entry;
16 17
@@ -84,6 +85,11 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
84 */ 85 */
85 private $trusted; 86 private $trusted;
86 87
88 /**
89 * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"})
90 */
91 protected $clients;
92
87 public function __construct() 93 public function __construct()
88 { 94 {
89 parent::__construct(); 95 parent::__construct();
@@ -240,4 +246,24 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
240 246
241 return false; 247 return false;
242 } 248 }
249
250 /**
251 * @param Client $client
252 *
253 * @return User
254 */
255 public function addClient(Client $client)
256 {
257 $this->clients[] = $client;
258
259 return $this;
260 }
261
262 /**
263 * @return ArrayCollection<Entry>
264 */
265 public function getClients()
266 {
267 return $this->clients;
268 }
243} 269}
diff --git a/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php b/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php
index 8e2f04e9..0bdd1cae 100644
--- a/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php
+++ b/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php
@@ -21,8 +21,10 @@ class CreateConfigListener implements EventSubscriberInterface
21 private $rssLimit; 21 private $rssLimit;
22 private $language; 22 private $language;
23 private $readingSpeed; 23 private $readingSpeed;
24 private $actionMarkAsRead;
25 private $listMode;
24 26
25 public function __construct(EntityManager $em, $theme, $itemsOnPage, $rssLimit, $language, $readingSpeed) 27 public function __construct(EntityManager $em, $theme, $itemsOnPage, $rssLimit, $language, $readingSpeed, $actionMarkAsRead, $listMode)
26 { 28 {
27 $this->em = $em; 29 $this->em = $em;
28 $this->theme = $theme; 30 $this->theme = $theme;
@@ -30,6 +32,8 @@ class CreateConfigListener implements EventSubscriberInterface
30 $this->rssLimit = $rssLimit; 32 $this->rssLimit = $rssLimit;
31 $this->language = $language; 33 $this->language = $language;
32 $this->readingSpeed = $readingSpeed; 34 $this->readingSpeed = $readingSpeed;
35 $this->actionMarkAsRead = $actionMarkAsRead;
36 $this->listMode = $listMode;
33 } 37 }
34 38
35 public static function getSubscribedEvents() 39 public static function getSubscribedEvents()
@@ -51,6 +55,8 @@ class CreateConfigListener implements EventSubscriberInterface
51 $config->setRssLimit($this->rssLimit); 55 $config->setRssLimit($this->rssLimit);
52 $config->setLanguage($this->language); 56 $config->setLanguage($this->language);
53 $config->setReadingSpeed($this->readingSpeed); 57 $config->setReadingSpeed($this->readingSpeed);
58 $config->setActionMarkAsRead($this->actionMarkAsRead);
59 $config->setListMode($this->listMode);
54 60
55 $this->em->persist($config); 61 $this->em->persist($config);
56 $this->em->flush(); 62 $this->em->flush();
diff --git a/src/Wallabag/UserBundle/Form/UserType.php b/src/Wallabag/UserBundle/Form/UserType.php
index cfa67793..d8cdbaf9 100644
--- a/src/Wallabag/UserBundle/Form/UserType.php
+++ b/src/Wallabag/UserBundle/Form/UserType.php
@@ -35,10 +35,6 @@ class UserType extends AbstractType
35 'required' => false, 35 'required' => false,
36 'label' => 'user.form.enabled_label', 36 'label' => 'user.form.enabled_label',
37 ]) 37 ])
38 ->add('locked', CheckboxType::class, [
39 'required' => false,
40 'label' => 'user.form.locked_label',
41 ])
42 ->add('twoFactorAuthentication', CheckboxType::class, [ 38 ->add('twoFactorAuthentication', CheckboxType::class, [
43 'required' => false, 39 'required' => false,
44 'label' => 'user.form.twofactor_label', 40 'label' => 'user.form.twofactor_label',
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php
index 009c4881..f913f52d 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.enabled = true')
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..72f6f12c 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"
@@ -31,5 +31,7 @@ services:
31 - "%wallabag_core.rss_limit%" 31 - "%wallabag_core.rss_limit%"
32 - "%wallabag_core.language%" 32 - "%wallabag_core.language%"
33 - "%wallabag_core.reading_speed%" 33 - "%wallabag_core.reading_speed%"
34 - "%wallabag_core.action_mark_as_read%"
35 - "%wallabag_core.list_mode%"
34 tags: 36 tags:
35 - { name: kernel.event_subscriber } 37 - { name: kernel.event_subscriber }
diff --git a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.de.yml b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.de.yml
new file mode 100644
index 00000000..4efaaab4
--- /dev/null
+++ b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.de.yml
@@ -0,0 +1,11 @@
1# Two factor mail
2auth_code:
3 on: 'an'
4 mailer:
5 subject: 'wallabag Authentifizierungcode'
6 body:
7 hello: "Hi %user%,"
8 first_para: "da du die Zwei-Faktor-Authentifizierung in deinem wallabag Konto aktiviert hast, und du dich gerade von einem neuen Gerät (Computer, Handy, etc.) anmeldest, senden wir dir einen Code, um deinen Zugriff zu prüfen."
9 second_para: "Hier ist der Code:"
10 support: "Bitte zögere nicht, Kontakt mit uns aufzunehmen falls du ein Problem hast:"
11 signature: "Das wallabag Team"
diff --git a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.en.yml b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.en.yml
index 85f2ea9c..ee0a27d5 100644
--- a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.en.yml
+++ b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.en.yml
@@ -2,7 +2,7 @@
2auth_code: 2auth_code:
3 on: 'on' 3 on: 'on'
4 mailer: 4 mailer:
5 subject: 'Wallabag authentication Code' 5 subject: 'wallabag authentication code'
6 body: 6 body:
7 hello: "Hi %user%," 7 hello: "Hi %user%,"
8 first_para: "Since you enable two factor authentication on your wallabag account and you just logged in from a new device (computer, phone, etc.), we send you a code to validate your connection." 8 first_para: "Since you enable two factor authentication on your wallabag account and you just logged in from a new device (computer, phone, etc.), we send you a code to validate your connection."
diff --git a/src/Wallabag/UserBundle/Resources/views/Manage/edit.html.twig b/src/Wallabag/UserBundle/Resources/views/Manage/edit.html.twig
index 67843f20..3ffd15f5 100644
--- a/src/Wallabag/UserBundle/Resources/views/Manage/edit.html.twig
+++ b/src/Wallabag/UserBundle/Resources/views/Manage/edit.html.twig
@@ -47,14 +47,6 @@
47 </div> 47 </div>
48 </div> 48 </div>
49 49
50 <div class="row">
51 <div class="input-field col s12">
52 {{ form_widget(edit_form.locked) }}
53 {{ form_label(edit_form.locked) }}
54 {{ form_errors(edit_form.locked) }}
55 </div>
56 </div>
57
58 {% if twofactor_auth %} 50 {% if twofactor_auth %}
59 <div class="row"> 51 <div class="row">
60 <div class="input-field col s12"> 52 <div class="input-field col s12">
diff --git a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig
index 996bdb1a..daba29e4 100644
--- a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig
+++ b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig
@@ -17,7 +17,6 @@
17 <th>{{ 'user.form.username_label'|trans }}</th> 17 <th>{{ 'user.form.username_label'|trans }}</th>
18 <th>{{ 'user.form.email_label'|trans }}</th> 18 <th>{{ 'user.form.email_label'|trans }}</th>
19 <th>{{ 'user.form.last_login_label'|trans }}</th> 19 <th>{{ 'user.form.last_login_label'|trans }}</th>
20 <th>{{ 'user.form.locked_label'|trans }}</th>
21 <th>{{ 'user.list.actions'|trans }}</th> 20 <th>{{ 'user.list.actions'|trans }}</th>
22 </tr> 21 </tr>
23 </thead> 22 </thead>
@@ -27,7 +26,6 @@
27 <td>{{ user.username }}</td> 26 <td>{{ user.username }}</td>
28 <td>{{ user.email }}</td> 27 <td>{{ user.email }}</td>
29 <td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td> 28 <td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td>
30 <td>{% if user.locked %}{{ 'user.list.yes'|trans }}{% else %}{{ 'user.list.no'|trans }}{% endif %}</td>
31 <td> 29 <td>
32 <a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a> 30 <a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a>
33 </td> 31 </td>
diff --git a/src/Wallabag/UserBundle/Resources/views/Resetting/checkEmail.html.twig b/src/Wallabag/UserBundle/Resources/views/Resetting/check_email.html.twig
index 66cbdc28..e9d46dcc 100644
--- a/src/Wallabag/UserBundle/Resources/views/Resetting/checkEmail.html.twig
+++ b/src/Wallabag/UserBundle/Resources/views/Resetting/check_email.html.twig
@@ -5,7 +5,7 @@
5{% block fos_user_content %} 5{% block fos_user_content %}
6<div class="card-content"> 6<div class="card-content">
7 <div class="row"> 7 <div class="row">
8 {{ 'resetting.check_email'|trans({'%email%': email}) }} 8 <p>{{ 'resetting.check_email'|trans({'%tokenLifetime%': tokenLifetime}) }}</p>
9 </div> 9 </div>
10</div> 10</div>
11{% endblock fos_user_content %} 11{% endblock fos_user_content %}
diff --git a/src/Wallabag/UserBundle/Resources/views/TwoFactor/email_auth_code.html.twig b/src/Wallabag/UserBundle/Resources/views/TwoFactor/email_auth_code.html.twig
index 3731f13b..5d1f22b7 100644
--- a/src/Wallabag/UserBundle/Resources/views/TwoFactor/email_auth_code.html.twig
+++ b/src/Wallabag/UserBundle/Resources/views/TwoFactor/email_auth_code.html.twig
@@ -74,7 +74,7 @@
74 74
75 <table cellpadding="0" cellspacing="0" border="0" align="center" id="card"> 75 <table cellpadding="0" cellspacing="0" border="0" align="center" id="card">
76 <tr> 76 <tr>
77 <td style="padding: 20px;" width="96px" valign="top"><img class="image_fix" src="{{ asset('bundles/wallabagcore/themes/material/img/logo-other_themes.png') }}" alt="logo" title="{{ wallabag_url }}" style="width: 96px; height: 96px;" /></td> 77 <td style="padding: 20px;" width="96px" valign="top"><img class="image_fix" src="{{ absolute_url(asset('bundles/wallabagcore/themes/_global/img/logo-other_themes.png')) }}" alt="logo" title="{{ wallabag_url }}" style="width: 96px; height: 96px;" /></td>
78 <td style="padding: 20px; padding-left: 0;" valign="top" id="cell_desc"> 78 <td style="padding: 20px; padding-left: 0;" valign="top" id="cell_desc">
79 <h1>wallabag</h1> 79 <h1>wallabag</h1>
80 <h5>{{ "auth_code.on"|trans({}, 'wallabag_user') }} {{ wallabag_url }}</h5> 80 <h5>{{ "auth_code.on"|trans({}, 'wallabag_user') }} {{ wallabag_url }}</h5>