diff options
Diffstat (limited to 'src')
117 files changed, 3810 insertions, 1037 deletions
diff --git a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php index c13a034f..2b4b0e8d 100644 --- a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php +++ b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php | |||
@@ -7,6 +7,8 @@ use Symfony\Component\HttpFoundation\JsonResponse; | |||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | 8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; |
9 | use Wallabag\AnnotationBundle\Entity\Annotation; | 9 | use Wallabag\AnnotationBundle\Entity\Annotation; |
10 | use Wallabag\AnnotationBundle\Form\EditAnnotationType; | ||
11 | use Wallabag\AnnotationBundle\Form\NewAnnotationType; | ||
10 | use Wallabag\CoreBundle\Entity\Entry; | 12 | use Wallabag\CoreBundle\Entity\Entry; |
11 | 13 | ||
12 | class WallabagAnnotationController extends FOSRestController | 14 | class WallabagAnnotationController extends FOSRestController |
@@ -49,25 +51,25 @@ class WallabagAnnotationController extends FOSRestController | |||
49 | $data = json_decode($request->getContent(), true); | 51 | $data = json_decode($request->getContent(), true); |
50 | 52 | ||
51 | $em = $this->getDoctrine()->getManager(); | 53 | $em = $this->getDoctrine()->getManager(); |
52 | |||
53 | $annotation = new Annotation($this->getUser()); | 54 | $annotation = new Annotation($this->getUser()); |
55 | $annotation->setEntry($entry); | ||
54 | 56 | ||
55 | $annotation->setText($data['text']); | 57 | $form = $this->get('form.factory')->createNamed('', NewAnnotationType::class, $annotation, [ |
56 | if (array_key_exists('quote', $data)) { | 58 | 'csrf_protection' => false, |
57 | $annotation->setQuote($data['quote']); | 59 | 'allow_extra_fields' => true, |
58 | } | 60 | ]); |
59 | if (array_key_exists('ranges', $data)) { | 61 | $form->submit($data); |
60 | $annotation->setRanges($data['ranges']); | ||
61 | } | ||
62 | 62 | ||
63 | $annotation->setEntry($entry); | 63 | if ($form->isValid()) { |
64 | $em->persist($annotation); | ||
65 | $em->flush(); | ||
64 | 66 | ||
65 | $em->persist($annotation); | 67 | $json = $this->get('serializer')->serialize($annotation, 'json'); |
66 | $em->flush(); | ||
67 | 68 | ||
68 | $json = $this->get('serializer')->serialize($annotation, 'json'); | 69 | return JsonResponse::fromJsonString($json); |
70 | } | ||
69 | 71 | ||
70 | return (new JsonResponse())->setJson($json); | 72 | return $form; |
71 | } | 73 | } |
72 | 74 | ||
73 | /** | 75 | /** |
@@ -86,16 +88,23 @@ class WallabagAnnotationController extends FOSRestController | |||
86 | { | 88 | { |
87 | $data = json_decode($request->getContent(), true); | 89 | $data = json_decode($request->getContent(), true); |
88 | 90 | ||
89 | if (!is_null($data['text'])) { | 91 | $form = $this->get('form.factory')->createNamed('', EditAnnotationType::class, $annotation, [ |
90 | $annotation->setText($data['text']); | 92 | 'csrf_protection' => false, |
91 | } | 93 | 'allow_extra_fields' => true, |
94 | ]); | ||
95 | $form->submit($data); | ||
92 | 96 | ||
93 | $em = $this->getDoctrine()->getManager(); | 97 | if ($form->isValid()) { |
94 | $em->flush(); | 98 | $em = $this->getDoctrine()->getManager(); |
99 | $em->persist($annotation); | ||
100 | $em->flush(); | ||
95 | 101 | ||
96 | $json = $this->get('serializer')->serialize($annotation, 'json'); | 102 | $json = $this->get('serializer')->serialize($annotation, 'json'); |
97 | 103 | ||
98 | return (new JsonResponse())->setJson($json); | 104 | return JsonResponse::fromJsonString($json); |
105 | } | ||
106 | |||
107 | return $form; | ||
99 | } | 108 | } |
100 | 109 | ||
101 | /** | 110 | /** |
diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php index 0838f5aa..c8e41649 100644 --- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php +++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php | |||
@@ -8,6 +8,7 @@ use JMS\Serializer\Annotation\Exclude; | |||
8 | use JMS\Serializer\Annotation\VirtualProperty; | 8 | use JMS\Serializer\Annotation\VirtualProperty; |
9 | use JMS\Serializer\Annotation\SerializedName; | 9 | use JMS\Serializer\Annotation\SerializedName; |
10 | use JMS\Serializer\Annotation\Groups; | 10 | use JMS\Serializer\Annotation\Groups; |
11 | use Symfony\Component\Validator\Constraints as Assert; | ||
11 | use Wallabag\UserBundle\Entity\User; | 12 | use Wallabag\UserBundle\Entity\User; |
12 | use Wallabag\CoreBundle\Entity\Entry; | 13 | use Wallabag\CoreBundle\Entity\Entry; |
13 | 14 | ||
@@ -56,7 +57,11 @@ class Annotation | |||
56 | /** | 57 | /** |
57 | * @var string | 58 | * @var string |
58 | * | 59 | * |
59 | * @ORM\Column(name="quote", type="string") | 60 | * @Assert\Length( |
61 | * max = 10000, | ||
62 | * maxMessage = "validator.quote_length_too_high" | ||
63 | * ) | ||
64 | * @ORM\Column(name="quote", type="text") | ||
60 | * | 65 | * |
61 | * @Groups({"entries_for_user", "export_all"}) | 66 | * @Groups({"entries_for_user", "export_all"}) |
62 | */ | 67 | */ |
diff --git a/src/Wallabag/AnnotationBundle/Form/EditAnnotationType.php b/src/Wallabag/AnnotationBundle/Form/EditAnnotationType.php new file mode 100644 index 00000000..3b587478 --- /dev/null +++ b/src/Wallabag/AnnotationBundle/Form/EditAnnotationType.php | |||
@@ -0,0 +1,18 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\AnnotationBundle\Form; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\FormBuilderInterface; | ||
7 | |||
8 | class EditAnnotationType extends AbstractType | ||
9 | { | ||
10 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
11 | { | ||
12 | $builder | ||
13 | ->add('text', null, [ | ||
14 | 'empty_data' => '', | ||
15 | ]) | ||
16 | ; | ||
17 | } | ||
18 | } | ||
diff --git a/src/Wallabag/AnnotationBundle/Form/NewAnnotationType.php b/src/Wallabag/AnnotationBundle/Form/NewAnnotationType.php new file mode 100644 index 00000000..c73c3ded --- /dev/null +++ b/src/Wallabag/AnnotationBundle/Form/NewAnnotationType.php | |||
@@ -0,0 +1,35 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\AnnotationBundle\Form; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\Extension\Core\Type\CollectionType; | ||
7 | use Symfony\Component\Form\FormBuilderInterface; | ||
8 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
9 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
10 | |||
11 | class NewAnnotationType extends AbstractType | ||
12 | { | ||
13 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
14 | { | ||
15 | $builder | ||
16 | ->add('text', null, [ | ||
17 | 'empty_data' => '', | ||
18 | ]) | ||
19 | ->add('quote', null, [ | ||
20 | 'empty_data' => null, | ||
21 | ]) | ||
22 | ->add('ranges', CollectionType::class, [ | ||
23 | 'entry_type' => RangeType::class, | ||
24 | 'allow_add' => true, | ||
25 | ]) | ||
26 | ; | ||
27 | } | ||
28 | |||
29 | public function configureOptions(OptionsResolver $resolver) | ||
30 | { | ||
31 | $resolver->setDefaults([ | ||
32 | 'data_class' => Annotation::class, | ||
33 | ]); | ||
34 | } | ||
35 | } | ||
diff --git a/src/Wallabag/AnnotationBundle/Form/RangeType.php b/src/Wallabag/AnnotationBundle/Form/RangeType.php new file mode 100644 index 00000000..0647375e --- /dev/null +++ b/src/Wallabag/AnnotationBundle/Form/RangeType.php | |||
@@ -0,0 +1,19 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\AnnotationBundle\Form; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\FormBuilderInterface; | ||
7 | |||
8 | class RangeType extends AbstractType | ||
9 | { | ||
10 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
11 | { | ||
12 | $builder | ||
13 | ->add('start') | ||
14 | ->add('startOffset') | ||
15 | ->add('end') | ||
16 | ->add('endOffset') | ||
17 | ; | ||
18 | } | ||
19 | } | ||
diff --git a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php index 8d3f07ee..da361308 100644 --- a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php +++ b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php | |||
@@ -122,4 +122,21 @@ class AnnotationRepository extends EntityRepository | |||
122 | ->setParameter('userId', $userId) | 122 | ->setParameter('userId', $userId) |
123 | ->execute(); | 123 | ->execute(); |
124 | } | 124 | } |
125 | |||
126 | /** | ||
127 | * Find all annotations related to archived entries. | ||
128 | * | ||
129 | * @param $userId | ||
130 | * | ||
131 | * @return mixed | ||
132 | */ | ||
133 | public function findAllArchivedEntriesByUser($userId) | ||
134 | { | ||
135 | return $this->createQueryBuilder('a') | ||
136 | ->leftJoin('a.entry', 'e') | ||
137 | ->where('a.user = :userid')->setParameter(':userid', $userId) | ||
138 | ->andWhere('e.isArchived = true') | ||
139 | ->getQuery() | ||
140 | ->getResult(); | ||
141 | } | ||
125 | } | 142 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php index 9cb1b626..9cb73f4c 100644 --- a/src/Wallabag/ApiBundle/Controller/DeveloperController.php +++ b/src/Wallabag/ApiBundle/Controller/DeveloperController.php | |||
@@ -43,7 +43,7 @@ class DeveloperController extends Controller | |||
43 | $clientForm->handleRequest($request); | 43 | $clientForm->handleRequest($request); |
44 | 44 | ||
45 | if ($clientForm->isSubmitted() && $clientForm->isValid()) { | 45 | if ($clientForm->isSubmitted() && $clientForm->isValid()) { |
46 | $client->setAllowedGrantTypes(['token', 'authorization_code', 'password', 'refresh_token']); | 46 | $client->setAllowedGrantTypes(['client_credentials', 'token', 'authorization_code', 'password', 'refresh_token']); |
47 | $em->persist($client); | 47 | $em->persist($client); |
48 | $em->flush(); | 48 | $em->flush(); |
49 | 49 | ||
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 54c1747c..768c4fdc 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -5,6 +5,7 @@ namespace Wallabag\ApiBundle\Controller; | |||
5 | use Hateoas\Configuration\Route; | 5 | use Hateoas\Configuration\Route; |
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | 6 | use Hateoas\Representation\Factory\PagerfantaFactory; |
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
8 | use Symfony\Component\HttpKernel\Exception\HttpException; | ||
8 | use Symfony\Component\HttpFoundation\Request; | 9 | use Symfony\Component\HttpFoundation\Request; |
9 | use Symfony\Component\HttpFoundation\JsonResponse; | 10 | use Symfony\Component\HttpFoundation\JsonResponse; |
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 11 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
@@ -41,12 +42,10 @@ class EntryRestController extends WallabagRestController | |||
41 | ->getRepository('WallabagCoreBundle:Entry') | 42 | ->getRepository('WallabagCoreBundle:Entry') |
42 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 43 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
43 | 44 | ||
44 | $results[$url] = false === $res ? false : true; | 45 | $results[$url] = $res instanceof Entry ? $res->getId() : false; |
45 | } | 46 | } |
46 | 47 | ||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | 48 | return $this->sendResponse($results); |
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | 49 | } |
51 | 50 | ||
52 | // let's see if it is a simple url? | 51 | // let's see if it is a simple url? |
@@ -60,11 +59,9 @@ class EntryRestController extends WallabagRestController | |||
60 | ->getRepository('WallabagCoreBundle:Entry') | 59 | ->getRepository('WallabagCoreBundle:Entry') |
61 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 60 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
62 | 61 | ||
63 | $exists = false === $res ? false : true; | 62 | $exists = $res instanceof Entry ? $res->getId() : false; |
64 | |||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
66 | 63 | ||
67 | return (new JsonResponse())->setJson($json); | 64 | return $this->sendResponse(['exists' => $exists]); |
68 | } | 65 | } |
69 | 66 | ||
70 | /** | 67 | /** |
@@ -80,6 +77,7 @@ class EntryRestController extends WallabagRestController | |||
80 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | 77 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, |
81 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | 78 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, |
82 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, | 79 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, |
80 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"}, | ||
83 | * } | 81 | * } |
84 | * ) | 82 | * ) |
85 | * | 83 | * |
@@ -91,6 +89,7 @@ class EntryRestController extends WallabagRestController | |||
91 | 89 | ||
92 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | 90 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); |
93 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); | 91 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); |
92 | $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public'); | ||
94 | $sort = $request->query->get('sort', 'created'); | 93 | $sort = $request->query->get('sort', 'created'); |
95 | $order = $request->query->get('order', 'desc'); | 94 | $order = $request->query->get('order', 'desc'); |
96 | $page = (int) $request->query->get('page', 1); | 95 | $page = (int) $request->query->get('page', 1); |
@@ -99,9 +98,16 @@ class EntryRestController extends WallabagRestController | |||
99 | $since = $request->query->get('since', 0); | 98 | $since = $request->query->get('since', 0); |
100 | 99 | ||
101 | /** @var \Pagerfanta\Pagerfanta $pager */ | 100 | /** @var \Pagerfanta\Pagerfanta $pager */ |
102 | $pager = $this->getDoctrine() | 101 | $pager = $this->get('wallabag_core.entry_repository')->findEntries( |
103 | ->getRepository('WallabagCoreBundle:Entry') | 102 | $this->getUser()->getId(), |
104 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | 103 | $isArchived, |
104 | $isStarred, | ||
105 | $isPublic, | ||
106 | $sort, | ||
107 | $order, | ||
108 | $since, | ||
109 | $tags | ||
110 | ); | ||
105 | 111 | ||
106 | $pager->setMaxPerPage($perPage); | 112 | $pager->setMaxPerPage($perPage); |
107 | $pager->setCurrentPage($page); | 113 | $pager->setCurrentPage($page); |
@@ -114,6 +120,7 @@ class EntryRestController extends WallabagRestController | |||
114 | [ | 120 | [ |
115 | 'archive' => $isArchived, | 121 | 'archive' => $isArchived, |
116 | 'starred' => $isStarred, | 122 | 'starred' => $isStarred, |
123 | 'public' => $isPublic, | ||
117 | 'sort' => $sort, | 124 | 'sort' => $sort, |
118 | 'order' => $order, | 125 | 'order' => $order, |
119 | 'page' => $page, | 126 | 'page' => $page, |
@@ -125,9 +132,7 @@ class EntryRestController extends WallabagRestController | |||
125 | ) | 132 | ) |
126 | ); | 133 | ); |
127 | 134 | ||
128 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | 135 | return $this->sendResponse($paginatedCollection); |
129 | |||
130 | return (new JsonResponse())->setJson($json); | ||
131 | } | 136 | } |
132 | 137 | ||
133 | /** | 138 | /** |
@@ -146,9 +151,7 @@ class EntryRestController extends WallabagRestController | |||
146 | $this->validateAuthentication(); | 151 | $this->validateAuthentication(); |
147 | $this->validateUserAccess($entry->getUser()->getId()); | 152 | $this->validateUserAccess($entry->getUser()->getId()); |
148 | 153 | ||
149 | $json = $this->get('serializer')->serialize($entry, 'json'); | 154 | return $this->sendResponse($entry); |
150 | |||
151 | return (new JsonResponse())->setJson($json); | ||
152 | } | 155 | } |
153 | 156 | ||
154 | /** | 157 | /** |
@@ -174,74 +177,153 @@ class EntryRestController extends WallabagRestController | |||
174 | } | 177 | } |
175 | 178 | ||
176 | /** | 179 | /** |
177 | * Create an entry. | 180 | * Handles an entries list and delete URL. |
178 | * | 181 | * |
179 | * @ApiDoc( | 182 | * @ApiDoc( |
180 | * parameters={ | 183 | * parameters={ |
181 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | 184 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} |
182 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
183 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
184 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
185 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
186 | * } | 185 | * } |
187 | * ) | 186 | * ) |
188 | * | 187 | * |
189 | * @return JsonResponse | 188 | * @return JsonResponse |
190 | */ | 189 | */ |
191 | public function postEntriesAction(Request $request) | 190 | public function deleteEntriesListAction(Request $request) |
192 | { | 191 | { |
193 | $this->validateAuthentication(); | 192 | $this->validateAuthentication(); |
194 | 193 | ||
195 | $url = $request->request->get('url'); | 194 | $urls = json_decode($request->query->get('urls', [])); |
196 | $title = $request->request->get('title'); | 195 | |
197 | $isArchived = $request->request->get('archive'); | 196 | if (empty($urls)) { |
198 | $isStarred = $request->request->get('starred'); | 197 | return $this->sendResponse([]); |
198 | } | ||
199 | 199 | ||
200 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | 200 | $results = []; |
201 | 201 | ||
202 | if (false === $entry) { | 202 | // handle multiple urls |
203 | $entry = new Entry($this->getUser()); | 203 | foreach ($urls as $key => $url) { |
204 | try { | 204 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
205 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | 205 | $url, |
206 | $entry, | 206 | $this->getUser()->getId() |
207 | $url | 207 | ); |
208 | ); | 208 | |
209 | } catch (\Exception $e) { | 209 | $results[$key]['url'] = $url; |
210 | $this->get('logger')->error('Error while saving an entry', [ | 210 | |
211 | 'exception' => $e, | 211 | if (false !== $entry) { |
212 | 'entry' => $entry, | 212 | $em = $this->getDoctrine()->getManager(); |
213 | ]); | 213 | $em->remove($entry); |
214 | $entry->setUrl($url); | 214 | $em->flush(); |
215 | |||
216 | // entry deleted, dispatch event about it! | ||
217 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
215 | } | 218 | } |
216 | } | ||
217 | 219 | ||
218 | if (!is_null($title)) { | 220 | $results[$key]['entry'] = $entry instanceof Entry ? true : false; |
219 | $entry->setTitle($title); | ||
220 | } | 221 | } |
221 | 222 | ||
222 | $tags = $request->request->get('tags', ''); | 223 | return $this->sendResponse($results); |
223 | if (!empty($tags)) { | 224 | } |
224 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 225 | |
226 | /** | ||
227 | * Handles an entries list and create URL. | ||
228 | * | ||
229 | * @ApiDoc( | ||
230 | * parameters={ | ||
231 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} | ||
232 | * } | ||
233 | * ) | ||
234 | * | ||
235 | * @return JsonResponse | ||
236 | * | ||
237 | * @throws HttpException When limit is reached | ||
238 | */ | ||
239 | public function postEntriesListAction(Request $request) | ||
240 | { | ||
241 | $this->validateAuthentication(); | ||
242 | |||
243 | $urls = json_decode($request->query->get('urls', [])); | ||
244 | |||
245 | $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); | ||
246 | |||
247 | if (count($urls) > $limit) { | ||
248 | throw new HttpException(400, 'API limit reached'); | ||
225 | } | 249 | } |
226 | 250 | ||
227 | if (!is_null($isStarred)) { | 251 | $results = []; |
228 | $entry->setStarred((bool) $isStarred); | 252 | if (empty($urls)) { |
253 | return $this->sendResponse($results); | ||
229 | } | 254 | } |
230 | 255 | ||
231 | if (!is_null($isArchived)) { | 256 | // handle multiple urls |
232 | $entry->setArchived((bool) $isArchived); | 257 | foreach ($urls as $key => $url) { |
258 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
259 | $url, | ||
260 | $this->getUser()->getId() | ||
261 | ); | ||
262 | |||
263 | $results[$key]['url'] = $url; | ||
264 | |||
265 | if (false === $entry) { | ||
266 | $entry = new Entry($this->getUser()); | ||
267 | |||
268 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url); | ||
269 | } | ||
270 | |||
271 | $em = $this->getDoctrine()->getManager(); | ||
272 | $em->persist($entry); | ||
273 | $em->flush(); | ||
274 | |||
275 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
276 | |||
277 | // entry saved, dispatch event about it! | ||
278 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
233 | } | 279 | } |
234 | 280 | ||
235 | $em = $this->getDoctrine()->getManager(); | 281 | return $this->sendResponse($results); |
236 | $em->persist($entry); | 282 | } |
237 | $em->flush(); | ||
238 | 283 | ||
239 | // entry saved, dispatch event about it! | 284 | /** |
240 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 285 | * Create an entry. |
286 | * | ||
287 | * If you want to provide the HTML content (which means wallabag won't fetch it from the url), you must provide `content`, `title` & `url` fields **non-empty**. | ||
288 | * Otherwise, content will be fetched as normal from the url and values will be overwritten. | ||
289 | * | ||
290 | * @ApiDoc( | ||
291 | * parameters={ | ||
292 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | ||
293 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
294 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
295 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
296 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
297 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
298 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
299 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
300 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
301 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
302 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, | ||
303 | * } | ||
304 | * ) | ||
305 | * | ||
306 | * @return JsonResponse | ||
307 | */ | ||
308 | public function postEntriesAction(Request $request) | ||
309 | { | ||
310 | $this->validateAuthentication(); | ||
241 | 311 | ||
242 | $json = $this->get('serializer')->serialize($entry, 'json'); | 312 | $url = $request->request->get('url'); |
243 | 313 | ||
244 | return (new JsonResponse())->setJson($json); | 314 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
315 | $url, | ||
316 | $this->getUser()->getId() | ||
317 | ); | ||
318 | |||
319 | if (false === $entry) { | ||
320 | $entry = new Entry($this->getUser()); | ||
321 | $entry->setUrl($url); | ||
322 | } | ||
323 | |||
324 | $this->upsertEntry($entry, $request); | ||
325 | |||
326 | return $this->sendResponse($entry); | ||
245 | } | 327 | } |
246 | 328 | ||
247 | /** | 329 | /** |
@@ -256,6 +338,12 @@ class EntryRestController extends WallabagRestController | |||
256 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | 338 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
257 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, | 339 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, |
258 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, | 340 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, |
341 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
342 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
343 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
344 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
345 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
346 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, | ||
259 | * } | 347 | * } |
260 | * ) | 348 | * ) |
261 | * | 349 | * |
@@ -266,33 +354,9 @@ class EntryRestController extends WallabagRestController | |||
266 | $this->validateAuthentication(); | 354 | $this->validateAuthentication(); |
267 | $this->validateUserAccess($entry->getUser()->getId()); | 355 | $this->validateUserAccess($entry->getUser()->getId()); |
268 | 356 | ||
269 | $title = $request->request->get('title'); | 357 | $this->upsertEntry($entry, $request, true); |
270 | $isArchived = $request->request->get('archive'); | ||
271 | $isStarred = $request->request->get('starred'); | ||
272 | |||
273 | if (!is_null($title)) { | ||
274 | $entry->setTitle($title); | ||
275 | } | ||
276 | |||
277 | if (!is_null($isArchived)) { | ||
278 | $entry->setArchived((bool) $isArchived); | ||
279 | } | ||
280 | |||
281 | if (!is_null($isStarred)) { | ||
282 | $entry->setStarred((bool) $isStarred); | ||
283 | } | ||
284 | |||
285 | $tags = $request->request->get('tags', ''); | ||
286 | if (!empty($tags)) { | ||
287 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
288 | } | ||
289 | |||
290 | $em = $this->getDoctrine()->getManager(); | ||
291 | $em->flush(); | ||
292 | |||
293 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
294 | 358 | ||
295 | return (new JsonResponse())->setJson($json); | 359 | return $this->sendResponse($entry); |
296 | } | 360 | } |
297 | 361 | ||
298 | /** | 362 | /** |
@@ -313,7 +377,7 @@ class EntryRestController extends WallabagRestController | |||
313 | $this->validateUserAccess($entry->getUser()->getId()); | 377 | $this->validateUserAccess($entry->getUser()->getId()); |
314 | 378 | ||
315 | try { | 379 | try { |
316 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | 380 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); |
317 | } catch (\Exception $e) { | 381 | } catch (\Exception $e) { |
318 | $this->get('logger')->error('Error while saving an entry', [ | 382 | $this->get('logger')->error('Error while saving an entry', [ |
319 | 'exception' => $e, | 383 | 'exception' => $e, |
@@ -335,9 +399,7 @@ class EntryRestController extends WallabagRestController | |||
335 | // entry saved, dispatch event about it! | 399 | // entry saved, dispatch event about it! |
336 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 400 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
337 | 401 | ||
338 | $json = $this->get('serializer')->serialize($entry, 'json'); | 402 | return $this->sendResponse($entry); |
339 | |||
340 | return (new JsonResponse())->setJson($json); | ||
341 | } | 403 | } |
342 | 404 | ||
343 | /** | 405 | /** |
@@ -363,9 +425,7 @@ class EntryRestController extends WallabagRestController | |||
363 | // entry deleted, dispatch event about it! | 425 | // entry deleted, dispatch event about it! |
364 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | 426 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
365 | 427 | ||
366 | $json = $this->get('serializer')->serialize($entry, 'json'); | 428 | return $this->sendResponse($entry); |
367 | |||
368 | return (new JsonResponse())->setJson($json); | ||
369 | } | 429 | } |
370 | 430 | ||
371 | /** | 431 | /** |
@@ -384,9 +444,7 @@ class EntryRestController extends WallabagRestController | |||
384 | $this->validateAuthentication(); | 444 | $this->validateAuthentication(); |
385 | $this->validateUserAccess($entry->getUser()->getId()); | 445 | $this->validateUserAccess($entry->getUser()->getId()); |
386 | 446 | ||
387 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | 447 | return $this->sendResponse($entry->getTags()); |
388 | |||
389 | return (new JsonResponse())->setJson($json); | ||
390 | } | 448 | } |
391 | 449 | ||
392 | /** | 450 | /** |
@@ -410,16 +468,14 @@ class EntryRestController extends WallabagRestController | |||
410 | 468 | ||
411 | $tags = $request->request->get('tags', ''); | 469 | $tags = $request->request->get('tags', ''); |
412 | if (!empty($tags)) { | 470 | if (!empty($tags)) { |
413 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 471 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); |
414 | } | 472 | } |
415 | 473 | ||
416 | $em = $this->getDoctrine()->getManager(); | 474 | $em = $this->getDoctrine()->getManager(); |
417 | $em->persist($entry); | 475 | $em->persist($entry); |
418 | $em->flush(); | 476 | $em->flush(); |
419 | 477 | ||
420 | $json = $this->get('serializer')->serialize($entry, 'json'); | 478 | return $this->sendResponse($entry); |
421 | |||
422 | return (new JsonResponse())->setJson($json); | ||
423 | } | 479 | } |
424 | 480 | ||
425 | /** | 481 | /** |
@@ -444,8 +500,198 @@ class EntryRestController extends WallabagRestController | |||
444 | $em->persist($entry); | 500 | $em->persist($entry); |
445 | $em->flush(); | 501 | $em->flush(); |
446 | 502 | ||
447 | $json = $this->get('serializer')->serialize($entry, 'json'); | 503 | return $this->sendResponse($entry); |
504 | } | ||
505 | |||
506 | /** | ||
507 | * Handles an entries list delete tags from them. | ||
508 | * | ||
509 | * @ApiDoc( | ||
510 | * parameters={ | ||
511 | * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} | ||
512 | * } | ||
513 | * ) | ||
514 | * | ||
515 | * @return JsonResponse | ||
516 | */ | ||
517 | public function deleteEntriesTagsListAction(Request $request) | ||
518 | { | ||
519 | $this->validateAuthentication(); | ||
520 | |||
521 | $list = json_decode($request->query->get('list', [])); | ||
522 | |||
523 | if (empty($list)) { | ||
524 | return $this->sendResponse([]); | ||
525 | } | ||
526 | |||
527 | // handle multiple urls | ||
528 | $results = []; | ||
529 | |||
530 | foreach ($list as $key => $element) { | ||
531 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
532 | $element->url, | ||
533 | $this->getUser()->getId() | ||
534 | ); | ||
535 | |||
536 | $results[$key]['url'] = $element->url; | ||
537 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
538 | |||
539 | $tags = $element->tags; | ||
540 | |||
541 | if (false !== $entry && !(empty($tags))) { | ||
542 | $tags = explode(',', $tags); | ||
543 | foreach ($tags as $label) { | ||
544 | $label = trim($label); | ||
545 | |||
546 | $tag = $this->getDoctrine() | ||
547 | ->getRepository('WallabagCoreBundle:Tag') | ||
548 | ->findOneByLabel($label); | ||
549 | |||
550 | if (false !== $tag) { | ||
551 | $entry->removeTag($tag); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | $em = $this->getDoctrine()->getManager(); | ||
556 | $em->persist($entry); | ||
557 | $em->flush(); | ||
558 | } | ||
559 | } | ||
560 | |||
561 | return $this->sendResponse($results); | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * Handles an entries list and add tags to them. | ||
566 | * | ||
567 | * @ApiDoc( | ||
568 | * parameters={ | ||
569 | * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} | ||
570 | * } | ||
571 | * ) | ||
572 | * | ||
573 | * @return JsonResponse | ||
574 | */ | ||
575 | public function postEntriesTagsListAction(Request $request) | ||
576 | { | ||
577 | $this->validateAuthentication(); | ||
578 | |||
579 | $list = json_decode($request->query->get('list', [])); | ||
580 | |||
581 | if (empty($list)) { | ||
582 | return $this->sendResponse([]); | ||
583 | } | ||
584 | |||
585 | $results = []; | ||
586 | |||
587 | // handle multiple urls | ||
588 | foreach ($list as $key => $element) { | ||
589 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
590 | $element->url, | ||
591 | $this->getUser()->getId() | ||
592 | ); | ||
593 | |||
594 | $results[$key]['url'] = $element->url; | ||
595 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
596 | |||
597 | $tags = $element->tags; | ||
598 | |||
599 | if (false !== $entry && !(empty($tags))) { | ||
600 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); | ||
601 | |||
602 | $em = $this->getDoctrine()->getManager(); | ||
603 | $em->persist($entry); | ||
604 | $em->flush(); | ||
605 | } | ||
606 | } | ||
607 | |||
608 | return $this->sendResponse($results); | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * Shortcut to send data serialized in json. | ||
613 | * | ||
614 | * @param mixed $data | ||
615 | * | ||
616 | * @return JsonResponse | ||
617 | */ | ||
618 | private function sendResponse($data) | ||
619 | { | ||
620 | $json = $this->get('serializer')->serialize($data, 'json'); | ||
448 | 621 | ||
449 | return (new JsonResponse())->setJson($json); | 622 | return (new JsonResponse())->setJson($json); |
450 | } | 623 | } |
624 | |||
625 | /** | ||
626 | * Update or Insert a new entry. | ||
627 | * | ||
628 | * @param Entry $entry | ||
629 | * @param Request $request | ||
630 | * @param bool $disableContentUpdate If we don't want the content to be update by fetching the url (used when patching instead of posting) | ||
631 | */ | ||
632 | private function upsertEntry(Entry $entry, Request $request, $disableContentUpdate = false) | ||
633 | { | ||
634 | $title = $request->request->get('title'); | ||
635 | $tags = $request->request->get('tags', []); | ||
636 | $isArchived = $request->request->get('archive'); | ||
637 | $isStarred = $request->request->get('starred'); | ||
638 | $isPublic = $request->request->get('public'); | ||
639 | $content = $request->request->get('content'); | ||
640 | $language = $request->request->get('language'); | ||
641 | $picture = $request->request->get('preview_picture'); | ||
642 | $publishedAt = $request->request->get('published_at'); | ||
643 | $authors = $request->request->get('authors', ''); | ||
644 | |||
645 | try { | ||
646 | $this->get('wallabag_core.content_proxy')->updateEntry( | ||
647 | $entry, | ||
648 | $entry->getUrl(), | ||
649 | [ | ||
650 | 'title' => !empty($title) ? $title : $entry->getTitle(), | ||
651 | 'html' => !empty($content) ? $content : $entry->getContent(), | ||
652 | 'url' => $entry->getUrl(), | ||
653 | 'language' => !empty($language) ? $language : $entry->getLanguage(), | ||
654 | 'date' => !empty($publishedAt) ? $publishedAt : $entry->getPublishedAt(), | ||
655 | // faking the open graph preview picture | ||
656 | 'open_graph' => [ | ||
657 | 'og_image' => !empty($picture) ? $picture : $entry->getPreviewPicture(), | ||
658 | ], | ||
659 | 'authors' => is_string($authors) ? explode(',', $authors) : $entry->getPublishedBy(), | ||
660 | ], | ||
661 | $disableContentUpdate | ||
662 | ); | ||
663 | } catch (\Exception $e) { | ||
664 | $this->get('logger')->error('Error while saving an entry', [ | ||
665 | 'exception' => $e, | ||
666 | 'entry' => $entry, | ||
667 | ]); | ||
668 | } | ||
669 | |||
670 | if (!is_null($isArchived)) { | ||
671 | $entry->setArchived((bool) $isArchived); | ||
672 | } | ||
673 | |||
674 | if (!is_null($isStarred)) { | ||
675 | $entry->setStarred((bool) $isStarred); | ||
676 | } | ||
677 | |||
678 | if (!empty($tags)) { | ||
679 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); | ||
680 | } | ||
681 | |||
682 | if (!is_null($isPublic)) { | ||
683 | if (true === (bool) $isPublic && null === $entry->getUid()) { | ||
684 | $entry->generateUid(); | ||
685 | } elseif (false === (bool) $isPublic) { | ||
686 | $entry->cleanUid(); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | $em = $this->getDoctrine()->getManager(); | ||
691 | $em->persist($entry); | ||
692 | $em->flush(); | ||
693 | |||
694 | // entry saved, dispatch event about it! | ||
695 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
696 | } | ||
451 | } | 697 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php index bc6d4e64..354187a0 100644 --- a/src/Wallabag/ApiBundle/Controller/TagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php | |||
@@ -31,7 +31,7 @@ class TagRestController extends WallabagRestController | |||
31 | } | 31 | } |
32 | 32 | ||
33 | /** | 33 | /** |
34 | * Permanently remove one tag from **every** entry. | 34 | * Permanently remove one tag from **every** entry by passing the Tag label. |
35 | * | 35 | * |
36 | * @ApiDoc( | 36 | * @ApiDoc( |
37 | * requirements={ | 37 | * requirements={ |
@@ -44,7 +44,7 @@ class TagRestController extends WallabagRestController | |||
44 | public function deleteTagLabelAction(Request $request) | 44 | public function deleteTagLabelAction(Request $request) |
45 | { | 45 | { |
46 | $this->validateAuthentication(); | 46 | $this->validateAuthentication(); |
47 | $label = $request->request->get('tag', ''); | 47 | $label = $request->get('tag', ''); |
48 | 48 | ||
49 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); | 49 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); |
50 | 50 | ||
@@ -78,7 +78,7 @@ class TagRestController extends WallabagRestController | |||
78 | { | 78 | { |
79 | $this->validateAuthentication(); | 79 | $this->validateAuthentication(); |
80 | 80 | ||
81 | $tagsLabels = $request->request->get('tags', ''); | 81 | $tagsLabels = $request->get('tags', ''); |
82 | 82 | ||
83 | $tags = []; | 83 | $tags = []; |
84 | 84 | ||
@@ -106,7 +106,7 @@ class TagRestController extends WallabagRestController | |||
106 | } | 106 | } |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * Permanently remove one tag from **every** entry. | 109 | * Permanently remove one tag from **every** entry by passing the Tag ID. |
110 | * | 110 | * |
111 | * @ApiDoc( | 111 | * @ApiDoc( |
112 | * requirements={ | 112 | * requirements={ |
diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php new file mode 100644 index 00000000..7471f5f6 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/UserRestController.php | |||
@@ -0,0 +1,157 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use FOS\UserBundle\Event\UserEvent; | ||
6 | use FOS\UserBundle\FOSUserEvents; | ||
7 | use JMS\Serializer\SerializationContext; | ||
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
9 | use Symfony\Component\HttpFoundation\Request; | ||
10 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | use Wallabag\ApiBundle\Entity\Client; | ||
13 | |||
14 | class UserRestController extends WallabagRestController | ||
15 | { | ||
16 | /** | ||
17 | * Retrieve current logged in user informations. | ||
18 | * | ||
19 | * @ApiDoc() | ||
20 | * | ||
21 | * @return JsonResponse | ||
22 | */ | ||
23 | public function getUserAction() | ||
24 | { | ||
25 | $this->validateAuthentication(); | ||
26 | |||
27 | return $this->sendUser($this->getUser()); | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Register an user and create a client. | ||
32 | * | ||
33 | * @ApiDoc( | ||
34 | * requirements={ | ||
35 | * {"name"="username", "dataType"="string", "required"=true, "description"="The user's username"}, | ||
36 | * {"name"="password", "dataType"="string", "required"=true, "description"="The user's password"}, | ||
37 | * {"name"="email", "dataType"="string", "required"=true, "description"="The user's email"}, | ||
38 | * {"name"="client_name", "dataType"="string", "required"=true, "description"="The client name (to be used by your app)"} | ||
39 | * } | ||
40 | * ) | ||
41 | * | ||
42 | * @todo Make this method (or the whole API) accessible only through https | ||
43 | * | ||
44 | * @return JsonResponse | ||
45 | */ | ||
46 | public function putUserAction(Request $request) | ||
47 | { | ||
48 | if (!$this->getParameter('fosuser_registration') || !$this->get('craue_config')->get('api_user_registration')) { | ||
49 | $json = $this->get('serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json'); | ||
50 | |||
51 | return (new JsonResponse()) | ||
52 | ->setJson($json) | ||
53 | ->setStatusCode(JsonResponse::HTTP_FORBIDDEN); | ||
54 | } | ||
55 | |||
56 | $userManager = $this->get('fos_user.user_manager'); | ||
57 | $user = $userManager->createUser(); | ||
58 | // user will be disabled BY DEFAULT to avoid spamming account to be enabled | ||
59 | $user->setEnabled(false); | ||
60 | |||
61 | $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ | ||
62 | 'csrf_protection' => false, | ||
63 | ]); | ||
64 | |||
65 | // simulate form submission | ||
66 | $form->submit([ | ||
67 | 'username' => $request->request->get('username'), | ||
68 | 'plainPassword' => [ | ||
69 | 'first' => $request->request->get('password'), | ||
70 | 'second' => $request->request->get('password'), | ||
71 | ], | ||
72 | 'email' => $request->request->get('email'), | ||
73 | ]); | ||
74 | |||
75 | if ($form->isSubmitted() && false === $form->isValid()) { | ||
76 | $view = $this->view($form, 400); | ||
77 | $view->setFormat('json'); | ||
78 | |||
79 | // handle errors in a more beautiful way than the default view | ||
80 | $data = json_decode($this->handleView($view)->getContent(), true)['children']; | ||
81 | $errors = []; | ||
82 | |||
83 | if (isset($data['username']['errors'])) { | ||
84 | $errors['username'] = $this->translateErrors($data['username']['errors']); | ||
85 | } | ||
86 | |||
87 | if (isset($data['email']['errors'])) { | ||
88 | $errors['email'] = $this->translateErrors($data['email']['errors']); | ||
89 | } | ||
90 | |||
91 | if (isset($data['plainPassword']['children']['first']['errors'])) { | ||
92 | $errors['password'] = $this->translateErrors($data['plainPassword']['children']['first']['errors']); | ||
93 | } | ||
94 | |||
95 | $json = $this->get('serializer')->serialize(['error' => $errors], 'json'); | ||
96 | |||
97 | return (new JsonResponse()) | ||
98 | ->setJson($json) | ||
99 | ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST); | ||
100 | } | ||
101 | |||
102 | // create a default client | ||
103 | $client = new Client($user); | ||
104 | $client->setName($request->request->get('client_name', 'Default client')); | ||
105 | |||
106 | $this->getDoctrine()->getManager()->persist($client); | ||
107 | |||
108 | $user->addClient($client); | ||
109 | |||
110 | $userManager->updateUser($user); | ||
111 | |||
112 | // dispatch a created event so the associated config will be created | ||
113 | $event = new UserEvent($user, $request); | ||
114 | $this->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); | ||
115 | |||
116 | return $this->sendUser($user, 'user_api_with_client', JsonResponse::HTTP_CREATED); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * Send user response. | ||
121 | * | ||
122 | * @param User $user | ||
123 | * @param string $group Used to define with serialized group might be used | ||
124 | * @param int $status HTTP Status code to send | ||
125 | * | ||
126 | * @return JsonResponse | ||
127 | */ | ||
128 | private function sendUser(User $user, $group = 'user_api', $status = JsonResponse::HTTP_OK) | ||
129 | { | ||
130 | $json = $this->get('serializer')->serialize( | ||
131 | $user, | ||
132 | 'json', | ||
133 | SerializationContext::create()->setGroups([$group]) | ||
134 | ); | ||
135 | |||
136 | return (new JsonResponse()) | ||
137 | ->setJson($json) | ||
138 | ->setStatusCode($status); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Translate errors message. | ||
143 | * | ||
144 | * @param array $errors | ||
145 | * | ||
146 | * @return array | ||
147 | */ | ||
148 | private function translateErrors($errors) | ||
149 | { | ||
150 | $translatedErrors = []; | ||
151 | foreach ($errors as $error) { | ||
152 | $translatedErrors[] = $this->get('translator')->trans($error); | ||
153 | } | ||
154 | |||
155 | return $translatedErrors; | ||
156 | } | ||
157 | } | ||
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php index 9ed9f980..c15fd3fa 100644 --- a/src/Wallabag/ApiBundle/Entity/Client.php +++ b/src/Wallabag/ApiBundle/Entity/Client.php | |||
@@ -5,6 +5,9 @@ namespace Wallabag\ApiBundle\Entity; | |||
5 | use Doctrine\ORM\Mapping as ORM; | 5 | use Doctrine\ORM\Mapping as ORM; |
6 | use FOS\OAuthServerBundle\Entity\Client as BaseClient; | 6 | use FOS\OAuthServerBundle\Entity\Client as BaseClient; |
7 | use Wallabag\UserBundle\Entity\User; | 7 | use Wallabag\UserBundle\Entity\User; |
8 | use JMS\Serializer\Annotation\Groups; | ||
9 | use JMS\Serializer\Annotation\SerializedName; | ||
10 | use JMS\Serializer\Annotation\VirtualProperty; | ||
8 | 11 | ||
9 | /** | 12 | /** |
10 | * @ORM\Table("oauth2_clients") | 13 | * @ORM\Table("oauth2_clients") |
@@ -23,6 +26,8 @@ class Client extends BaseClient | |||
23 | * @var string | 26 | * @var string |
24 | * | 27 | * |
25 | * @ORM\Column(name="name", type="text", nullable=false) | 28 | * @ORM\Column(name="name", type="text", nullable=false) |
29 | * | ||
30 | * @Groups({"user_api_with_client"}) | ||
26 | */ | 31 | */ |
27 | protected $name; | 32 | protected $name; |
28 | 33 | ||
@@ -37,6 +42,14 @@ class Client extends BaseClient | |||
37 | protected $accessTokens; | 42 | protected $accessTokens; |
38 | 43 | ||
39 | /** | 44 | /** |
45 | * @var string | ||
46 | * | ||
47 | * @SerializedName("client_secret") | ||
48 | * @Groups({"user_api_with_client"}) | ||
49 | */ | ||
50 | protected $secret; | ||
51 | |||
52 | /** | ||
40 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="clients") | 53 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="clients") |
41 | */ | 54 | */ |
42 | private $user; | 55 | private $user; |
@@ -78,4 +91,14 @@ class Client extends BaseClient | |||
78 | { | 91 | { |
79 | return $this->user; | 92 | return $this->user; |
80 | } | 93 | } |
94 | |||
95 | /** | ||
96 | * @VirtualProperty | ||
97 | * @SerializedName("client_id") | ||
98 | * @Groups({"user_api_with_client"}) | ||
99 | */ | ||
100 | public function getClientId() | ||
101 | { | ||
102 | return $this->getId().'_'.$this->getRandomId(); | ||
103 | } | ||
81 | } | 104 | } |
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index 57d37f4b..c0283e71 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml | |||
@@ -17,3 +17,8 @@ misc: | |||
17 | type: rest | 17 | type: rest |
18 | resource: "WallabagApiBundle:WallabagRest" | 18 | resource: "WallabagApiBundle:WallabagRest" |
19 | name_prefix: api_ | 19 | name_prefix: api_ |
20 | |||
21 | user: | ||
22 | type: rest | ||
23 | resource: "WallabagApiBundle:UserRest" | ||
24 | name_prefix: api_ | ||
diff --git a/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php new file mode 100644 index 00000000..74da1e5f --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php | |||
@@ -0,0 +1,119 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
7 | use Symfony\Component\Console\Input\InputArgument; | ||
8 | use Symfony\Component\Console\Input\InputInterface; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | use Wallabag\CoreBundle\Entity\Entry; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | |||
13 | class CleanDuplicatesCommand extends ContainerAwareCommand | ||
14 | { | ||
15 | /** @var OutputInterface */ | ||
16 | protected $output; | ||
17 | |||
18 | protected $duplicates = 0; | ||
19 | |||
20 | protected function configure() | ||
21 | { | ||
22 | $this | ||
23 | ->setName('wallabag:clean-duplicates') | ||
24 | ->setDescription('Cleans the database for duplicates') | ||
25 | ->setHelp('This command helps you to clean your articles list in case of duplicates') | ||
26 | ->addArgument( | ||
27 | 'username', | ||
28 | InputArgument::OPTIONAL, | ||
29 | 'User to clean' | ||
30 | ); | ||
31 | } | ||
32 | |||
33 | protected function execute(InputInterface $input, OutputInterface $output) | ||
34 | { | ||
35 | $this->output = $output; | ||
36 | |||
37 | $username = $input->getArgument('username'); | ||
38 | |||
39 | if ($username) { | ||
40 | try { | ||
41 | $user = $this->getUser($username); | ||
42 | $this->cleanDuplicates($user); | ||
43 | } catch (NoResultException $e) { | ||
44 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $username)); | ||
45 | |||
46 | return 1; | ||
47 | } | ||
48 | } else { | ||
49 | $users = $this->getContainer()->get('wallabag_user.user_repository')->findAll(); | ||
50 | |||
51 | $output->writeln(sprintf('Cleaning through %d user accounts', count($users))); | ||
52 | |||
53 | foreach ($users as $user) { | ||
54 | $output->writeln(sprintf('Processing user %s', $user->getUsername())); | ||
55 | $this->cleanDuplicates($user); | ||
56 | } | ||
57 | $output->writeln(sprintf('Finished cleaning. %d duplicates found in total', $this->duplicates)); | ||
58 | } | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * @param User $user | ||
65 | */ | ||
66 | private function cleanDuplicates(User $user) | ||
67 | { | ||
68 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | ||
69 | $repo = $this->getContainer()->get('wallabag_core.entry_repository'); | ||
70 | |||
71 | $entries = $repo->getAllEntriesIdAndUrl($user->getId()); | ||
72 | |||
73 | $duplicatesCount = 0; | ||
74 | $urls = []; | ||
75 | foreach ($entries as $entry) { | ||
76 | $url = $this->similarUrl($entry['url']); | ||
77 | |||
78 | /* @var $entry Entry */ | ||
79 | if (in_array($url, $urls)) { | ||
80 | ++$duplicatesCount; | ||
81 | |||
82 | $em->remove($repo->find($entry['id'])); | ||
83 | $em->flush(); // Flushing at the end of the loop would require the instance not being online | ||
84 | } else { | ||
85 | $urls[] = $entry['url']; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | $this->duplicates += $duplicatesCount; | ||
90 | |||
91 | $this->output->writeln(sprintf('Cleaned %d duplicates for user %s', $duplicatesCount, $user->getUserName())); | ||
92 | } | ||
93 | |||
94 | private function similarUrl($url) | ||
95 | { | ||
96 | if (in_array(substr($url, -1), ['/', '#'])) { // get rid of "/" and "#" and the end of urls | ||
97 | return substr($url, 0, strlen($url)); | ||
98 | } | ||
99 | |||
100 | return $url; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * Fetches a user from its username. | ||
105 | * | ||
106 | * @param string $username | ||
107 | * | ||
108 | * @return \Wallabag\UserBundle\Entity\User | ||
109 | */ | ||
110 | private function getUser($username) | ||
111 | { | ||
112 | return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); | ||
113 | } | ||
114 | |||
115 | private function getDoctrine() | ||
116 | { | ||
117 | return $this->getContainer()->get('doctrine'); | ||
118 | } | ||
119 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/ExportCommand.php b/src/Wallabag/CoreBundle/Command/ExportCommand.php index e3d3b399..ebb2b4cf 100644 --- a/src/Wallabag/CoreBundle/Command/ExportCommand.php +++ b/src/Wallabag/CoreBundle/Command/ExportCommand.php | |||
@@ -32,15 +32,14 @@ class ExportCommand extends ContainerAwareCommand | |||
32 | protected function execute(InputInterface $input, OutputInterface $output) | 32 | protected function execute(InputInterface $input, OutputInterface $output) |
33 | { | 33 | { |
34 | try { | 34 | try { |
35 | $user = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($input->getArgument('username')); | 35 | $user = $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($input->getArgument('username')); |
36 | } catch (NoResultException $e) { | 36 | } catch (NoResultException $e) { |
37 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); | 37 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); |
38 | 38 | ||
39 | return 1; | 39 | return 1; |
40 | } | 40 | } |
41 | 41 | ||
42 | $entries = $this->getDoctrine() | 42 | $entries = $this->getContainer()->get('wallabag_core.entry_repository') |
43 | ->getRepository('WallabagCoreBundle:Entry') | ||
44 | ->getBuilderForAllByUser($user->getId()) | 43 | ->getBuilderForAllByUser($user->getId()) |
45 | ->getQuery() | 44 | ->getQuery() |
46 | ->getResult(); | 45 | ->getResult(); |
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php index f0738b91..eb725a59 100644 --- a/src/Wallabag/CoreBundle/Command/InstallCommand.php +++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php | |||
@@ -63,6 +63,7 @@ class InstallCommand extends ContainerAwareCommand | |||
63 | ->setupDatabase() | 63 | ->setupDatabase() |
64 | ->setupAdmin() | 64 | ->setupAdmin() |
65 | ->setupConfig() | 65 | ->setupConfig() |
66 | ->runMigrations() | ||
66 | ; | 67 | ; |
67 | 68 | ||
68 | $output->writeln('<info>wallabag has been successfully installed.</info>'); | 69 | $output->writeln('<info>wallabag has been successfully installed.</info>'); |
@@ -71,7 +72,7 @@ class InstallCommand extends ContainerAwareCommand | |||
71 | 72 | ||
72 | protected function checkRequirements() | 73 | protected function checkRequirements() |
73 | { | 74 | { |
74 | $this->defaultOutput->writeln('<info><comment>Step 1 of 4.</comment> Checking system requirements.</info>'); | 75 | $this->defaultOutput->writeln('<info><comment>Step 1 of 5.</comment> Checking system requirements.</info>'); |
75 | $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); | 76 | $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); |
76 | 77 | ||
77 | $rows = []; | 78 | $rows = []; |
@@ -175,11 +176,11 @@ class InstallCommand extends ContainerAwareCommand | |||
175 | 176 | ||
176 | protected function setupDatabase() | 177 | protected function setupDatabase() |
177 | { | 178 | { |
178 | $this->defaultOutput->writeln('<info><comment>Step 2 of 4.</comment> Setting up database.</info>'); | 179 | $this->defaultOutput->writeln('<info><comment>Step 2 of 5.</comment> Setting up database.</info>'); |
179 | 180 | ||
180 | // user want to reset everything? Don't care about what is already here | 181 | // user want to reset everything? Don't care about what is already here |
181 | if (true === $this->defaultInput->getOption('reset')) { | 182 | if (true === $this->defaultInput->getOption('reset')) { |
182 | $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache'); | 183 | $this->defaultOutput->writeln('Dropping database, creating database and schema, clearing the cache'); |
183 | 184 | ||
184 | $this | 185 | $this |
185 | ->runCommand('doctrine:database:drop', ['--force' => true]) | 186 | ->runCommand('doctrine:database:drop', ['--force' => true]) |
@@ -211,7 +212,7 @@ class InstallCommand extends ContainerAwareCommand | |||
211 | $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); | 212 | $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); |
212 | 213 | ||
213 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | 214 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { |
214 | $this->defaultOutput->writeln('Droping database, creating database and schema'); | 215 | $this->defaultOutput->writeln('Dropping database, creating database and schema'); |
215 | 216 | ||
216 | $this | 217 | $this |
217 | ->runCommand('doctrine:database:drop', ['--force' => true]) | 218 | ->runCommand('doctrine:database:drop', ['--force' => true]) |
@@ -221,7 +222,7 @@ class InstallCommand extends ContainerAwareCommand | |||
221 | } elseif ($this->isSchemaPresent()) { | 222 | } elseif ($this->isSchemaPresent()) { |
222 | $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); | 223 | $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); |
223 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | 224 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { |
224 | $this->defaultOutput->writeln('Droping schema and creating schema'); | 225 | $this->defaultOutput->writeln('Dropping schema and creating schema'); |
225 | 226 | ||
226 | $this | 227 | $this |
227 | ->runCommand('doctrine:schema:drop', ['--force' => true]) | 228 | ->runCommand('doctrine:schema:drop', ['--force' => true]) |
@@ -246,7 +247,7 @@ class InstallCommand extends ContainerAwareCommand | |||
246 | 247 | ||
247 | protected function setupAdmin() | 248 | protected function setupAdmin() |
248 | { | 249 | { |
249 | $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>'); | 250 | $this->defaultOutput->writeln('<info><comment>Step 3 of 5.</comment> Administration setup.</info>'); |
250 | 251 | ||
251 | $questionHelper = $this->getHelperSet()->get('question'); | 252 | $questionHelper = $this->getHelperSet()->get('question'); |
252 | $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); | 253 | $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); |
@@ -285,161 +286,13 @@ class InstallCommand extends ContainerAwareCommand | |||
285 | 286 | ||
286 | protected function setupConfig() | 287 | protected function setupConfig() |
287 | { | 288 | { |
288 | $this->defaultOutput->writeln('<info><comment>Step 4 of 4.</comment> Config setup.</info>'); | 289 | $this->defaultOutput->writeln('<info><comment>Step 4 of 5.</comment> Config setup.</info>'); |
289 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | 290 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); |
290 | 291 | ||
291 | // cleanup before insert new stuff | 292 | // cleanup before insert new stuff |
292 | $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); | 293 | $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); |
293 | 294 | ||
294 | $settings = [ | 295 | foreach ($this->getContainer()->getParameter('wallabag_core.default_internal_settings') as $setting) { |
295 | [ | ||
296 | 'name' => 'share_public', | ||
297 | 'value' => '1', | ||
298 | 'section' => 'entry', | ||
299 | ], | ||
300 | [ | ||
301 | 'name' => 'carrot', | ||
302 | 'value' => '1', | ||
303 | 'section' => 'entry', | ||
304 | ], | ||
305 | [ | ||
306 | 'name' => 'share_diaspora', | ||
307 | 'value' => '1', | ||
308 | 'section' => 'entry', | ||
309 | ], | ||
310 | [ | ||
311 | 'name' => 'diaspora_url', | ||
312 | 'value' => 'http://diasporapod.com', | ||
313 | 'section' => 'entry', | ||
314 | ], | ||
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 | [ | ||
326 | 'name' => 'share_shaarli', | ||
327 | 'value' => '1', | ||
328 | 'section' => 'entry', | ||
329 | ], | ||
330 | [ | ||
331 | 'name' => 'shaarli_url', | ||
332 | 'value' => 'http://myshaarli.com', | ||
333 | 'section' => 'entry', | ||
334 | ], | ||
335 | [ | ||
336 | 'name' => 'share_mail', | ||
337 | 'value' => '1', | ||
338 | 'section' => 'entry', | ||
339 | ], | ||
340 | [ | ||
341 | 'name' => 'share_twitter', | ||
342 | 'value' => '1', | ||
343 | 'section' => 'entry', | ||
344 | ], | ||
345 | [ | ||
346 | 'name' => 'export_epub', | ||
347 | 'value' => '1', | ||
348 | 'section' => 'export', | ||
349 | ], | ||
350 | [ | ||
351 | 'name' => 'export_mobi', | ||
352 | 'value' => '1', | ||
353 | 'section' => 'export', | ||
354 | ], | ||
355 | [ | ||
356 | 'name' => 'export_pdf', | ||
357 | 'value' => '1', | ||
358 | 'section' => 'export', | ||
359 | ], | ||
360 | [ | ||
361 | 'name' => 'export_csv', | ||
362 | 'value' => '1', | ||
363 | 'section' => 'export', | ||
364 | ], | ||
365 | [ | ||
366 | 'name' => 'export_json', | ||
367 | 'value' => '1', | ||
368 | 'section' => 'export', | ||
369 | ], | ||
370 | [ | ||
371 | 'name' => 'export_txt', | ||
372 | 'value' => '1', | ||
373 | 'section' => 'export', | ||
374 | ], | ||
375 | [ | ||
376 | 'name' => 'export_xml', | ||
377 | 'value' => '1', | ||
378 | 'section' => 'export', | ||
379 | ], | ||
380 | [ | ||
381 | 'name' => 'import_with_redis', | ||
382 | 'value' => '0', | ||
383 | 'section' => 'import', | ||
384 | ], | ||
385 | [ | ||
386 | 'name' => 'import_with_rabbitmq', | ||
387 | 'value' => '0', | ||
388 | 'section' => 'import', | ||
389 | ], | ||
390 | [ | ||
391 | 'name' => 'show_printlink', | ||
392 | 'value' => '1', | ||
393 | 'section' => 'entry', | ||
394 | ], | ||
395 | [ | ||
396 | 'name' => 'wallabag_support_url', | ||
397 | 'value' => 'https://www.wallabag.org/pages/support.html', | ||
398 | 'section' => 'misc', | ||
399 | ], | ||
400 | [ | ||
401 | 'name' => 'wallabag_url', | ||
402 | 'value' => '', | ||
403 | 'section' => 'misc', | ||
404 | ], | ||
405 | [ | ||
406 | 'name' => 'piwik_enabled', | ||
407 | 'value' => '0', | ||
408 | 'section' => 'analytics', | ||
409 | ], | ||
410 | [ | ||
411 | 'name' => 'piwik_host', | ||
412 | 'value' => 'v2.wallabag.org', | ||
413 | 'section' => 'analytics', | ||
414 | ], | ||
415 | [ | ||
416 | 'name' => 'piwik_site_id', | ||
417 | 'value' => '1', | ||
418 | 'section' => 'analytics', | ||
419 | ], | ||
420 | [ | ||
421 | 'name' => 'demo_mode_enabled', | ||
422 | 'value' => '0', | ||
423 | 'section' => 'misc', | ||
424 | ], | ||
425 | [ | ||
426 | 'name' => 'demo_mode_username', | ||
427 | 'value' => 'wallabag', | ||
428 | 'section' => 'misc', | ||
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 | ], | ||
440 | ]; | ||
441 | |||
442 | foreach ($settings as $setting) { | ||
443 | $newSetting = new Setting(); | 296 | $newSetting = new Setting(); |
444 | $newSetting->setName($setting['name']); | 297 | $newSetting->setName($setting['name']); |
445 | $newSetting->setValue($setting['value']); | 298 | $newSetting->setValue($setting['value']); |
@@ -454,6 +307,16 @@ class InstallCommand extends ContainerAwareCommand | |||
454 | return $this; | 307 | return $this; |
455 | } | 308 | } |
456 | 309 | ||
310 | protected function runMigrations() | ||
311 | { | ||
312 | $this->defaultOutput->writeln('<info><comment>Step 5 of 5.</comment> Run migrations.</info>'); | ||
313 | |||
314 | $this | ||
315 | ->runCommand('doctrine:migrations:migrate', ['--no-interaction' => true]); | ||
316 | |||
317 | return $this; | ||
318 | } | ||
319 | |||
457 | /** | 320 | /** |
458 | * Run a command. | 321 | * Run a command. |
459 | * | 322 | * |
@@ -480,20 +343,18 @@ class InstallCommand extends ContainerAwareCommand | |||
480 | $output = new BufferedOutput(); | 343 | $output = new BufferedOutput(); |
481 | $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); | 344 | $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); |
482 | 345 | ||
346 | // PDO does not always close the connection after Doctrine commands. | ||
347 | // See https://github.com/symfony/symfony/issues/11750. | ||
348 | $this->getContainer()->get('doctrine')->getManager()->getConnection()->close(); | ||
349 | |||
483 | if (0 !== $exitCode) { | 350 | if (0 !== $exitCode) { |
484 | $this->getApplication()->setAutoExit(true); | 351 | $this->getApplication()->setAutoExit(true); |
485 | 352 | ||
486 | $this->defaultOutput->writeln(''); | 353 | throw new \RuntimeException( |
487 | $this->defaultOutput->writeln('<error>The command "'.$command.'" generates some errors: </error>'); | 354 | 'The command "'.$command."\" generates some errors: \n\n" |
488 | $this->defaultOutput->writeln($output->fetch()); | 355 | .$output->fetch()); |
489 | |||
490 | die(); | ||
491 | } | 356 | } |
492 | 357 | ||
493 | // PDO does not always close the connection after Doctrine commands. | ||
494 | // See https://github.com/symfony/symfony/issues/11750. | ||
495 | $this->getContainer()->get('doctrine')->getManager()->getConnection()->close(); | ||
496 | |||
497 | return $this; | 358 | return $this; |
498 | } | 359 | } |
499 | 360 | ||
diff --git a/src/Wallabag/CoreBundle/Command/ShowUserCommand.php b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php new file mode 100644 index 00000000..eef04988 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php | |||
@@ -0,0 +1,77 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
7 | use Symfony\Component\Console\Input\InputArgument; | ||
8 | use Symfony\Component\Console\Input\InputInterface; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | use Wallabag\UserBundle\Entity\User; | ||
11 | |||
12 | class ShowUserCommand extends ContainerAwareCommand | ||
13 | { | ||
14 | /** @var OutputInterface */ | ||
15 | protected $output; | ||
16 | |||
17 | protected function configure() | ||
18 | { | ||
19 | $this | ||
20 | ->setName('wallabag:user:show') | ||
21 | ->setDescription('Show user details') | ||
22 | ->setHelp('This command shows the details for an user') | ||
23 | ->addArgument( | ||
24 | 'username', | ||
25 | InputArgument::REQUIRED, | ||
26 | 'User to show details for' | ||
27 | ); | ||
28 | } | ||
29 | |||
30 | protected function execute(InputInterface $input, OutputInterface $output) | ||
31 | { | ||
32 | $this->output = $output; | ||
33 | |||
34 | $username = $input->getArgument('username'); | ||
35 | |||
36 | try { | ||
37 | $user = $this->getUser($username); | ||
38 | $this->showUser($user); | ||
39 | } catch (NoResultException $e) { | ||
40 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $username)); | ||
41 | |||
42 | return 1; | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * @param User $user | ||
50 | */ | ||
51 | private function showUser(User $user) | ||
52 | { | ||
53 | $this->output->writeln(sprintf('Username : %s', $user->getUsername())); | ||
54 | $this->output->writeln(sprintf('Email : %s', $user->getEmail())); | ||
55 | $this->output->writeln(sprintf('Display name : %s', $user->getName())); | ||
56 | $this->output->writeln(sprintf('Creation date : %s', $user->getCreatedAt()->format('Y-m-d H:i:s'))); | ||
57 | $this->output->writeln(sprintf('Last login : %s', $user->getLastLogin() !== null ? $user->getLastLogin()->format('Y-m-d H:i:s') : 'never')); | ||
58 | $this->output->writeln(sprintf('2FA activated: %s', $user->isTwoFactorAuthentication() ? 'yes' : 'no')); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Fetches a user from its username. | ||
63 | * | ||
64 | * @param string $username | ||
65 | * | ||
66 | * @return \Wallabag\UserBundle\Entity\User | ||
67 | */ | ||
68 | private function getUser($username) | ||
69 | { | ||
70 | return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); | ||
71 | } | ||
72 | |||
73 | private function getDoctrine() | ||
74 | { | ||
75 | return $this->getContainer()->get('doctrine'); | ||
76 | } | ||
77 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php index 3f9bb04d..9843674e 100644 --- a/src/Wallabag/CoreBundle/Command/TagAllCommand.php +++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php | |||
@@ -59,7 +59,7 @@ class TagAllCommand extends ContainerAwareCommand | |||
59 | */ | 59 | */ |
60 | private function getUser($username) | 60 | private function getUser($username) |
61 | { | 61 | { |
62 | return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); | 62 | return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); |
63 | } | 63 | } |
64 | 64 | ||
65 | private function getDoctrine() | 65 | private function getDoctrine() |
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 907bf78e..d4170d39 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php | |||
@@ -151,9 +151,8 @@ class ConfigController extends Controller | |||
151 | 'token' => $config->getRssToken(), | 151 | 'token' => $config->getRssToken(), |
152 | ], | 152 | ], |
153 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), | 153 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), |
154 | 'wallabag_url' => $this->get('craue_config')->get('wallabag_url'), | 154 | 'wallabag_url' => $this->getParameter('domain_name'), |
155 | 'enabled_users' => $this->getDoctrine() | 155 | 'enabled_users' => $this->get('wallabag_user.user_repository') |
156 | ->getRepository('WallabagUserBundle:User') | ||
157 | ->getSumEnabledUsers(), | 156 | ->getSumEnabledUsers(), |
158 | ]); | 157 | ]); |
159 | } | 158 | } |
@@ -248,18 +247,27 @@ class ConfigController extends Controller | |||
248 | break; | 247 | break; |
249 | 248 | ||
250 | case 'entries': | 249 | case 'entries': |
251 | // SQLite doesn't care about cascading remove, so we need to manually remove associated stuf | 250 | // SQLite doesn't care about cascading remove, so we need to manually remove associated stuff |
252 | // otherwise they won't be removed ... | 251 | // otherwise they won't be removed ... |
253 | if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { | 252 | if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { |
254 | $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); | 253 | $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); |
255 | } | 254 | } |
256 | 255 | ||
257 | // manually remove tags to avoid orphan tag | 256 | // manually remove tags to avoid orphan tag |
258 | $this->removeAllTagsByUserId($this->getUser()->getId()); | 257 | $this->removeAllTagsByUserId($this->getUser()->getId()); |
259 | 258 | ||
260 | $this->getDoctrine() | 259 | $this->get('wallabag_core.entry_repository')->removeAllByUserId($this->getUser()->getId()); |
261 | ->getRepository('WallabagCoreBundle:Entry') | 260 | break; |
262 | ->removeAllByUserId($this->getUser()->getId()); | 261 | case 'archived': |
262 | if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { | ||
263 | $this->removeAnnotationsForArchivedByUserId($this->getUser()->getId()); | ||
264 | } | ||
265 | |||
266 | // manually remove tags to avoid orphan tag | ||
267 | $this->removeTagsForArchivedByUserId($this->getUser()->getId()); | ||
268 | |||
269 | $this->get('wallabag_core.entry_repository')->removeArchivedByUserId($this->getUser()->getId()); | ||
270 | break; | ||
263 | } | 271 | } |
264 | 272 | ||
265 | $this->get('session')->getFlashBag()->add( | 273 | $this->get('session')->getFlashBag()->add( |
@@ -271,20 +279,18 @@ class ConfigController extends Controller | |||
271 | } | 279 | } |
272 | 280 | ||
273 | /** | 281 | /** |
274 | * Remove all tags for a given user and cleanup orphan tags. | 282 | * Remove all tags for given tags and a given user and cleanup orphan tags. |
275 | * | 283 | * |
276 | * @param int $userId | 284 | * @param array $tags |
285 | * @param int $userId | ||
277 | */ | 286 | */ |
278 | private function removeAllTagsByUserId($userId) | 287 | private function removeAllTagsByStatusAndUserId($tags, $userId) |
279 | { | 288 | { |
280 | $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId); | ||
281 | |||
282 | if (empty($tags)) { | 289 | if (empty($tags)) { |
283 | return; | 290 | return; |
284 | } | 291 | } |
285 | 292 | ||
286 | $this->getDoctrine() | 293 | $this->get('wallabag_core.entry_repository') |
287 | ->getRepository('WallabagCoreBundle:Entry') | ||
288 | ->removeTags($userId, $tags); | 294 | ->removeTags($userId, $tags); |
289 | 295 | ||
290 | // cleanup orphan tags | 296 | // cleanup orphan tags |
@@ -300,6 +306,43 @@ class ConfigController extends Controller | |||
300 | } | 306 | } |
301 | 307 | ||
302 | /** | 308 | /** |
309 | * Remove all tags for a given user and cleanup orphan tags. | ||
310 | * | ||
311 | * @param int $userId | ||
312 | */ | ||
313 | private function removeAllTagsByUserId($userId) | ||
314 | { | ||
315 | $tags = $this->get('wallabag_core.tag_repository')->findAllTags($userId); | ||
316 | $this->removeAllTagsByStatusAndUserId($tags, $userId); | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * Remove all tags for a given user and cleanup orphan tags. | ||
321 | * | ||
322 | * @param int $userId | ||
323 | */ | ||
324 | private function removeTagsForArchivedByUserId($userId) | ||
325 | { | ||
326 | $tags = $this->get('wallabag_core.tag_repository')->findForArchivedArticlesByUser($userId); | ||
327 | $this->removeAllTagsByStatusAndUserId($tags, $userId); | ||
328 | } | ||
329 | |||
330 | private function removeAnnotationsForArchivedByUserId($userId) | ||
331 | { | ||
332 | $em = $this->getDoctrine()->getManager(); | ||
333 | |||
334 | $archivedEntriesAnnotations = $this->getDoctrine() | ||
335 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
336 | ->findAllArchivedEntriesByUser($userId); | ||
337 | |||
338 | foreach ($archivedEntriesAnnotations as $archivedEntriesAnnotation) { | ||
339 | $em->remove($archivedEntriesAnnotation); | ||
340 | } | ||
341 | |||
342 | $em->flush(); | ||
343 | } | ||
344 | |||
345 | /** | ||
303 | * Validate that a rule can be edited/deleted by the current user. | 346 | * Validate that a rule can be edited/deleted by the current user. |
304 | * | 347 | * |
305 | * @param TaggingRule $rule | 348 | * @param TaggingRule $rule |
@@ -344,8 +387,7 @@ class ConfigController extends Controller | |||
344 | */ | 387 | */ |
345 | public function deleteAccountAction(Request $request) | 388 | public function deleteAccountAction(Request $request) |
346 | { | 389 | { |
347 | $enabledUsers = $this->getDoctrine() | 390 | $enabledUsers = $this->get('wallabag_user.user_repository') |
348 | ->getRepository('WallabagUserBundle:User') | ||
349 | ->getSumEnabledUsers(); | 391 | ->getSumEnabledUsers(); |
350 | 392 | ||
351 | if ($enabledUsers <= 1) { | 393 | if ($enabledUsers <= 1) { |
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php index f7398e69..fafa49f1 100644 --- a/src/Wallabag/CoreBundle/Controller/EntryController.php +++ b/src/Wallabag/CoreBundle/Controller/EntryController.php | |||
@@ -53,22 +53,17 @@ class EntryController extends Controller | |||
53 | 53 | ||
54 | /** | 54 | /** |
55 | * Fetch content and update entry. | 55 | * Fetch content and update entry. |
56 | * In case it fails, entry will return to avod loosing the data. | 56 | * In case it fails, $entry->getContent will return an error message. |
57 | * | 57 | * |
58 | * @param Entry $entry | 58 | * @param Entry $entry |
59 | * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded | 59 | * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded |
60 | * | ||
61 | * @return Entry | ||
62 | */ | 60 | */ |
63 | private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') | 61 | private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') |
64 | { | 62 | { |
65 | // put default title in case of fetching content failed | ||
66 | $entry->setTitle('No title found'); | ||
67 | |||
68 | $message = 'flashes.entry.notice.'.$prefixMessage; | 63 | $message = 'flashes.entry.notice.'.$prefixMessage; |
69 | 64 | ||
70 | try { | 65 | try { |
71 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | 66 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); |
72 | } catch (\Exception $e) { | 67 | } catch (\Exception $e) { |
73 | $this->get('logger')->error('Error while saving an entry', [ | 68 | $this->get('logger')->error('Error while saving an entry', [ |
74 | 'exception' => $e, | 69 | 'exception' => $e, |
@@ -79,8 +74,6 @@ class EntryController extends Controller | |||
79 | } | 74 | } |
80 | 75 | ||
81 | $this->get('session')->getFlashBag()->add('notice', $message); | 76 | $this->get('session')->getFlashBag()->add('notice', $message); |
82 | |||
83 | return $entry; | ||
84 | } | 77 | } |
85 | 78 | ||
86 | /** | 79 | /** |
@@ -227,7 +220,7 @@ class EntryController extends Controller | |||
227 | public function showUnreadAction(Request $request, $page) | 220 | public function showUnreadAction(Request $request, $page) |
228 | { | 221 | { |
229 | // load the quickstart if no entry in database | 222 | // load the quickstart if no entry in database |
230 | if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUsername($this->getUser()->getId()) == 0) { | 223 | if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUser($this->getUser()->getId()) == 0) { |
231 | return $this->redirect($this->generateUrl('quickstart')); | 224 | return $this->redirect($this->generateUrl('quickstart')); |
232 | } | 225 | } |
233 | 226 | ||
@@ -321,8 +314,7 @@ class EntryController extends Controller | |||
321 | 314 | ||
322 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | 315 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); |
323 | 316 | ||
324 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') | 317 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter); |
325 | ->prepare($pagerAdapter, $page); | ||
326 | 318 | ||
327 | try { | 319 | try { |
328 | $entries->setCurrentPage($page); | 320 | $entries->setCurrentPage($page); |
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php index abc3336a..fda04cfb 100644 --- a/src/Wallabag/CoreBundle/Controller/ExportController.php +++ b/src/Wallabag/CoreBundle/Controller/ExportController.php | |||
@@ -7,7 +7,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | 8 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
9 | use Wallabag\CoreBundle\Entity\Entry; | 9 | use Wallabag\CoreBundle\Entity\Entry; |
10 | use Wallabag\CoreBundle\Entity\Tag; | ||
11 | 10 | ||
12 | /** | 11 | /** |
13 | * The try/catch can be removed once all formats will be implemented. | 12 | * The try/catch can be removed once all formats will be implemented. |
@@ -57,16 +56,17 @@ class ExportController extends Controller | |||
57 | { | 56 | { |
58 | $method = ucfirst($category); | 57 | $method = ucfirst($category); |
59 | $methodBuilder = 'getBuilderFor'.$method.'ByUser'; | 58 | $methodBuilder = 'getBuilderFor'.$method.'ByUser'; |
59 | $repository = $this->get('wallabag_core.entry_repository'); | ||
60 | 60 | ||
61 | if ($category == 'tag_entries') { | 61 | if ($category == 'tag_entries') { |
62 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneBySlug($request->query->get('tag')); | 62 | $tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag')); |
63 | 63 | ||
64 | $entries = $this->getDoctrine() | 64 | $entries = $repository->findAllByTagId( |
65 | ->getRepository('WallabagCoreBundle:Entry') | 65 | $this->getUser()->getId(), |
66 | ->findAllByTagId($this->getUser()->getId(), $tag->getId()); | 66 | $tag->getId() |
67 | ); | ||
67 | } else { | 68 | } else { |
68 | $entries = $this->getDoctrine() | 69 | $entries = $repository |
69 | ->getRepository('WallabagCoreBundle:Entry') | ||
70 | ->$methodBuilder($this->getUser()->getId()) | 70 | ->$methodBuilder($this->getUser()->getId()) |
71 | ->getQuery() | 71 | ->getQuery() |
72 | ->getResult(); | 72 | ->getResult(); |
diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php index 92f18707..e87dd9a1 100644 --- a/src/Wallabag/CoreBundle/Controller/RssController.php +++ b/src/Wallabag/CoreBundle/Controller/RssController.php | |||
@@ -3,13 +3,16 @@ | |||
3 | namespace Wallabag\CoreBundle\Controller; | 3 | namespace Wallabag\CoreBundle\Controller; |
4 | 4 | ||
5 | use Pagerfanta\Adapter\DoctrineORMAdapter; | 5 | use Pagerfanta\Adapter\DoctrineORMAdapter; |
6 | use Pagerfanta\Adapter\ArrayAdapter; | ||
6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | 7 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; |
7 | use Pagerfanta\Pagerfanta; | 8 | use Pagerfanta\Pagerfanta; |
8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | 9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; |
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 10 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
10 | use Symfony\Component\HttpFoundation\Request; | 11 | use Symfony\Component\HttpFoundation\Request; |
12 | use Symfony\Component\HttpFoundation\Response; | ||
11 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 13 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
12 | use Wallabag\CoreBundle\Entity\Entry; | 14 | use Wallabag\CoreBundle\Entity\Entry; |
15 | use Wallabag\CoreBundle\Entity\Tag; | ||
13 | use Wallabag\UserBundle\Entity\User; | 16 | use Wallabag\UserBundle\Entity\User; |
14 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 17 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
15 | 18 | ||
@@ -23,7 +26,7 @@ class RssController extends Controller | |||
23 | * | 26 | * |
24 | * @return \Symfony\Component\HttpFoundation\Response | 27 | * @return \Symfony\Component\HttpFoundation\Response |
25 | */ | 28 | */ |
26 | public function showUnreadAction(Request $request, User $user) | 29 | public function showUnreadRSSAction(Request $request, User $user) |
27 | { | 30 | { |
28 | return $this->showEntries('unread', $user, $request->query->get('page', 1)); | 31 | return $this->showEntries('unread', $user, $request->query->get('page', 1)); |
29 | } | 32 | } |
@@ -31,12 +34,12 @@ class RssController extends Controller | |||
31 | /** | 34 | /** |
32 | * Shows read entries for current user. | 35 | * Shows read entries for current user. |
33 | * | 36 | * |
34 | * @Route("/{username}/{token}/archive.xml", name="archive_rss") | 37 | * @Route("/{username}/{token}/archive.xml", name="archive_rss", defaults={"_format"="xml"}) |
35 | * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") | 38 | * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") |
36 | * | 39 | * |
37 | * @return \Symfony\Component\HttpFoundation\Response | 40 | * @return \Symfony\Component\HttpFoundation\Response |
38 | */ | 41 | */ |
39 | public function showArchiveAction(Request $request, User $user) | 42 | public function showArchiveRSSAction(Request $request, User $user) |
40 | { | 43 | { |
41 | return $this->showEntries('archive', $user, $request->query->get('page', 1)); | 44 | return $this->showEntries('archive', $user, $request->query->get('page', 1)); |
42 | } | 45 | } |
@@ -44,17 +47,89 @@ class RssController extends Controller | |||
44 | /** | 47 | /** |
45 | * Shows starred entries for current user. | 48 | * Shows starred entries for current user. |
46 | * | 49 | * |
47 | * @Route("/{username}/{token}/starred.xml", name="starred_rss") | 50 | * @Route("/{username}/{token}/starred.xml", name="starred_rss", defaults={"_format"="xml"}) |
48 | * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") | 51 | * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") |
49 | * | 52 | * |
50 | * @return \Symfony\Component\HttpFoundation\Response | 53 | * @return \Symfony\Component\HttpFoundation\Response |
51 | */ | 54 | */ |
52 | public function showStarredAction(Request $request, User $user) | 55 | public function showStarredRSSAction(Request $request, User $user) |
53 | { | 56 | { |
54 | return $this->showEntries('starred', $user, $request->query->get('page', 1)); | 57 | return $this->showEntries('starred', $user, $request->query->get('page', 1)); |
55 | } | 58 | } |
56 | 59 | ||
57 | /** | 60 | /** |
61 | * Shows all entries for current user. | ||
62 | * | ||
63 | * @Route("/{username}/{token}/all.xml", name="all_rss", defaults={"_format"="xml"}) | ||
64 | * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") | ||
65 | * | ||
66 | * @return \Symfony\Component\HttpFoundation\Response | ||
67 | */ | ||
68 | public function showAllRSSAction(Request $request, User $user) | ||
69 | { | ||
70 | return $this->showEntries('all', $user, $request->query->get('page', 1)); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Shows entries associated to a tag for current user. | ||
75 | * | ||
76 | * @Route("/{username}/{token}/tags/{slug}.xml", name="tag_rss", defaults={"_format"="xml"}) | ||
77 | * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") | ||
78 | * @ParamConverter("tag", options={"mapping": {"slug": "slug"}}) | ||
79 | * | ||
80 | * @return \Symfony\Component\HttpFoundation\Response | ||
81 | */ | ||
82 | public function showTagsAction(Request $request, User $user, Tag $tag) | ||
83 | { | ||
84 | $page = $request->query->get('page', 1); | ||
85 | |||
86 | $url = $this->generateUrl( | ||
87 | 'tag_rss', | ||
88 | [ | ||
89 | 'username' => $user->getUsername(), | ||
90 | 'token' => $user->getConfig()->getRssToken(), | ||
91 | 'slug' => $tag->getSlug(), | ||
92 | ], | ||
93 | UrlGeneratorInterface::ABSOLUTE_URL | ||
94 | ); | ||
95 | |||
96 | $entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId( | ||
97 | $user->getId(), | ||
98 | $tag->getId() | ||
99 | ); | ||
100 | |||
101 | $pagerAdapter = new ArrayAdapter($entriesByTag); | ||
102 | |||
103 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare( | ||
104 | $pagerAdapter, | ||
105 | $user | ||
106 | ); | ||
107 | |||
108 | if (null === $entries) { | ||
109 | throw $this->createNotFoundException('No entries found?'); | ||
110 | } | ||
111 | |||
112 | try { | ||
113 | $entries->setCurrentPage($page); | ||
114 | } catch (OutOfRangeCurrentPageException $e) { | ||
115 | if ($page > 1) { | ||
116 | return $this->redirect($url.'?page='.$entries->getNbPages(), 302); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return $this->render( | ||
121 | '@WallabagCore/themes/common/Entry/entries.xml.twig', | ||
122 | [ | ||
123 | 'url_html' => $this->generateUrl('tag_entries', ['slug' => $tag->getSlug()], UrlGeneratorInterface::ABSOLUTE_URL), | ||
124 | 'type' => 'tag ('.$tag->getLabel().')', | ||
125 | 'url' => $url, | ||
126 | 'entries' => $entries, | ||
127 | ], | ||
128 | new Response('', 200, ['Content-Type' => 'application/rss+xml']) | ||
129 | ); | ||
130 | } | ||
131 | |||
132 | /** | ||
58 | * Global method to retrieve entries depending on the given type | 133 | * Global method to retrieve entries depending on the given type |
59 | * It returns the response to be send. | 134 | * It returns the response to be send. |
60 | * | 135 | * |
@@ -66,7 +141,7 @@ class RssController extends Controller | |||
66 | */ | 141 | */ |
67 | private function showEntries($type, User $user, $page = 1) | 142 | private function showEntries($type, User $user, $page = 1) |
68 | { | 143 | { |
69 | $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); | 144 | $repository = $this->get('wallabag_core.entry_repository'); |
70 | 145 | ||
71 | switch ($type) { | 146 | switch ($type) { |
72 | case 'starred': | 147 | case 'starred': |
@@ -81,6 +156,10 @@ class RssController extends Controller | |||
81 | $qb = $repository->getBuilderForUnreadByUser($user->getId()); | 156 | $qb = $repository->getBuilderForUnreadByUser($user->getId()); |
82 | break; | 157 | break; |
83 | 158 | ||
159 | case 'all': | ||
160 | $qb = $repository->getBuilderForAllByUser($user->getId()); | ||
161 | break; | ||
162 | |||
84 | default: | 163 | default: |
85 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); | 164 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); |
86 | } | 165 | } |
@@ -108,10 +187,15 @@ class RssController extends Controller | |||
108 | } | 187 | } |
109 | } | 188 | } |
110 | 189 | ||
111 | return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ | 190 | return $this->render( |
112 | 'type' => $type, | 191 | '@WallabagCore/themes/common/Entry/entries.xml.twig', |
113 | 'url' => $url, | 192 | [ |
114 | 'entries' => $entries, | 193 | 'url_html' => $this->generateUrl($type, [], UrlGeneratorInterface::ABSOLUTE_URL), |
115 | ]); | 194 | 'type' => $type, |
195 | 'url' => $url, | ||
196 | 'entries' => $entries, | ||
197 | ], | ||
198 | new Response('', 200, ['Content-Type' => 'application/rss+xml']) | ||
199 | ); | ||
116 | } | 200 | } |
117 | } | 201 | } |
diff --git a/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php b/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php new file mode 100644 index 00000000..98781dab --- /dev/null +++ b/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php | |||
@@ -0,0 +1,174 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Controller; | ||
4 | |||
5 | use Symfony\Component\HttpFoundation\Request; | ||
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | ||
8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | ||
9 | use Wallabag\UserBundle\Entity\User; | ||
10 | use Wallabag\CoreBundle\Entity\SiteCredential; | ||
11 | |||
12 | /** | ||
13 | * SiteCredential controller. | ||
14 | * | ||
15 | * @Route("/site-credentials") | ||
16 | */ | ||
17 | class SiteCredentialController extends Controller | ||
18 | { | ||
19 | /** | ||
20 | * Lists all User entities. | ||
21 | * | ||
22 | * @Route("/", name="site_credentials_index") | ||
23 | * @Method("GET") | ||
24 | */ | ||
25 | public function indexAction() | ||
26 | { | ||
27 | $credentials = $this->get('wallabag_core.site_credential_repository')->findByUser($this->getUser()); | ||
28 | |||
29 | return $this->render('WallabagCoreBundle:SiteCredential:index.html.twig', [ | ||
30 | 'credentials' => $credentials, | ||
31 | ]); | ||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Creates a new site credential entity. | ||
36 | * | ||
37 | * @Route("/new", name="site_credentials_new") | ||
38 | * @Method({"GET", "POST"}) | ||
39 | * | ||
40 | * @param Request $request | ||
41 | * | ||
42 | * @return \Symfony\Component\HttpFoundation\Response | ||
43 | */ | ||
44 | public function newAction(Request $request) | ||
45 | { | ||
46 | $credential = new SiteCredential($this->getUser()); | ||
47 | |||
48 | $form = $this->createForm('Wallabag\CoreBundle\Form\Type\SiteCredentialType', $credential); | ||
49 | $form->handleRequest($request); | ||
50 | |||
51 | if ($form->isSubmitted() && $form->isValid()) { | ||
52 | $credential->setUsername($this->get('wallabag_core.helper.crypto_proxy')->crypt($credential->getUsername())); | ||
53 | $credential->setPassword($this->get('wallabag_core.helper.crypto_proxy')->crypt($credential->getPassword())); | ||
54 | |||
55 | $em = $this->getDoctrine()->getManager(); | ||
56 | $em->persist($credential); | ||
57 | $em->flush(); | ||
58 | |||
59 | $this->get('session')->getFlashBag()->add( | ||
60 | 'notice', | ||
61 | $this->get('translator')->trans('flashes.site_credential.notice.added', ['%host%' => $credential->getHost()]) | ||
62 | ); | ||
63 | |||
64 | return $this->redirectToRoute('site_credentials_index'); | ||
65 | } | ||
66 | |||
67 | return $this->render('WallabagCoreBundle:SiteCredential:new.html.twig', [ | ||
68 | 'credential' => $credential, | ||
69 | 'form' => $form->createView(), | ||
70 | ]); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Displays a form to edit an existing site credential entity. | ||
75 | * | ||
76 | * @Route("/{id}/edit", name="site_credentials_edit") | ||
77 | * @Method({"GET", "POST"}) | ||
78 | * | ||
79 | * @param Request $request | ||
80 | * @param SiteCredential $siteCredential | ||
81 | * | ||
82 | * @return \Symfony\Component\HttpFoundation\Response | ||
83 | */ | ||
84 | public function editAction(Request $request, SiteCredential $siteCredential) | ||
85 | { | ||
86 | $this->checkUserAction($siteCredential); | ||
87 | |||
88 | $deleteForm = $this->createDeleteForm($siteCredential); | ||
89 | $editForm = $this->createForm('Wallabag\CoreBundle\Form\Type\SiteCredentialType', $siteCredential); | ||
90 | $editForm->handleRequest($request); | ||
91 | |||
92 | if ($editForm->isSubmitted() && $editForm->isValid()) { | ||
93 | $siteCredential->setUsername($this->get('wallabag_core.helper.crypto_proxy')->crypt($siteCredential->getUsername())); | ||
94 | $siteCredential->setPassword($this->get('wallabag_core.helper.crypto_proxy')->crypt($siteCredential->getPassword())); | ||
95 | |||
96 | $em = $this->getDoctrine()->getManager(); | ||
97 | $em->persist($siteCredential); | ||
98 | $em->flush(); | ||
99 | |||
100 | $this->get('session')->getFlashBag()->add( | ||
101 | 'notice', | ||
102 | $this->get('translator')->trans('flashes.site_credential.notice.updated', ['%host%' => $siteCredential->getHost()]) | ||
103 | ); | ||
104 | |||
105 | return $this->redirectToRoute('site_credentials_index'); | ||
106 | } | ||
107 | |||
108 | return $this->render('WallabagCoreBundle:SiteCredential:edit.html.twig', [ | ||
109 | 'credential' => $siteCredential, | ||
110 | 'edit_form' => $editForm->createView(), | ||
111 | 'delete_form' => $deleteForm->createView(), | ||
112 | ]); | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Deletes a site credential entity. | ||
117 | * | ||
118 | * @Route("/{id}", name="site_credentials_delete") | ||
119 | * @Method("DELETE") | ||
120 | * | ||
121 | * @param Request $request | ||
122 | * @param SiteCredential $siteCredential | ||
123 | * | ||
124 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
125 | */ | ||
126 | public function deleteAction(Request $request, SiteCredential $siteCredential) | ||
127 | { | ||
128 | $this->checkUserAction($siteCredential); | ||
129 | |||
130 | $form = $this->createDeleteForm($siteCredential); | ||
131 | $form->handleRequest($request); | ||
132 | |||
133 | if ($form->isSubmitted() && $form->isValid()) { | ||
134 | $this->get('session')->getFlashBag()->add( | ||
135 | 'notice', | ||
136 | $this->get('translator')->trans('flashes.site_credential.notice.deleted', ['%host%' => $siteCredential->getHost()]) | ||
137 | ); | ||
138 | |||
139 | $em = $this->getDoctrine()->getManager(); | ||
140 | $em->remove($siteCredential); | ||
141 | $em->flush(); | ||
142 | } | ||
143 | |||
144 | return $this->redirectToRoute('site_credentials_index'); | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * Creates a form to delete a site credential entity. | ||
149 | * | ||
150 | * @param SiteCredential $siteCredential The site credential entity | ||
151 | * | ||
152 | * @return \Symfony\Component\Form\Form The form | ||
153 | */ | ||
154 | private function createDeleteForm(SiteCredential $siteCredential) | ||
155 | { | ||
156 | return $this->createFormBuilder() | ||
157 | ->setAction($this->generateUrl('site_credentials_delete', ['id' => $siteCredential->getId()])) | ||
158 | ->setMethod('DELETE') | ||
159 | ->getForm() | ||
160 | ; | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * Check if the logged user can manage the given site credential. | ||
165 | * | ||
166 | * @param SiteCredential $siteCredential The site credential entity | ||
167 | */ | ||
168 | private function checkUserAction(SiteCredential $siteCredential) | ||
169 | { | ||
170 | if (null === $this->getUser() || $this->getUser()->getId() != $siteCredential->getUser()->getId()) { | ||
171 | throw $this->createAccessDeniedException('You can not access this site credential.'); | ||
172 | } | ||
173 | } | ||
174 | } | ||
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index 8a093289..a8b1eadd 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php | |||
@@ -28,7 +28,7 @@ class TagController extends Controller | |||
28 | $form->handleRequest($request); | 28 | $form->handleRequest($request); |
29 | 29 | ||
30 | if ($form->isSubmitted() && $form->isValid()) { | 30 | if ($form->isSubmitted() && $form->isValid()) { |
31 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry( | 31 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry( |
32 | $entry, | 32 | $entry, |
33 | $form->get('label')->getData() | 33 | $form->get('label')->getData() |
34 | ); | 34 | ); |
@@ -70,7 +70,7 @@ class TagController extends Controller | |||
70 | $em->flush(); | 70 | $em->flush(); |
71 | } | 71 | } |
72 | 72 | ||
73 | $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer')); | 73 | $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer'), '', true); |
74 | 74 | ||
75 | return $this->redirect($redirectUrl); | 75 | return $this->redirect($redirectUrl); |
76 | } | 76 | } |
@@ -84,16 +84,17 @@ class TagController extends Controller | |||
84 | */ | 84 | */ |
85 | public function showTagAction() | 85 | public function showTagAction() |
86 | { | 86 | { |
87 | $tags = $this->getDoctrine() | 87 | $repository = $this->get('wallabag_core.entry_repository'); |
88 | ->getRepository('WallabagCoreBundle:Tag') | 88 | $tags = $this->get('wallabag_core.tag_repository') |
89 | ->findAllTags($this->getUser()->getId()); | 89 | ->findAllTags($this->getUser()->getId()); |
90 | 90 | ||
91 | $flatTags = []; | 91 | $flatTags = []; |
92 | 92 | ||
93 | foreach ($tags as $tag) { | 93 | foreach ($tags as $tag) { |
94 | $nbEntries = $this->getDoctrine() | 94 | $nbEntries = $repository->countAllEntriesByUserIdAndTagId( |
95 | ->getRepository('WallabagCoreBundle:Entry') | 95 | $this->getUser()->getId(), |
96 | ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId()); | 96 | $tag->getId() |
97 | ); | ||
97 | 98 | ||
98 | $flatTags[] = [ | 99 | $flatTags[] = [ |
99 | 'id' => $tag->getId(), | 100 | 'id' => $tag->getId(), |
@@ -119,14 +120,14 @@ class TagController extends Controller | |||
119 | */ | 120 | */ |
120 | public function showEntriesForTagAction(Tag $tag, $page, Request $request) | 121 | public function showEntriesForTagAction(Tag $tag, $page, Request $request) |
121 | { | 122 | { |
122 | $entriesByTag = $this->getDoctrine() | 123 | $entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId( |
123 | ->getRepository('WallabagCoreBundle:Entry') | 124 | $this->getUser()->getId(), |
124 | ->findAllByTagId($this->getUser()->getId(), $tag->getId()); | 125 | $tag->getId() |
126 | ); | ||
125 | 127 | ||
126 | $pagerAdapter = new ArrayAdapter($entriesByTag); | 128 | $pagerAdapter = new ArrayAdapter($entriesByTag); |
127 | 129 | ||
128 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') | 130 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter); |
129 | ->prepare($pagerAdapter, $page); | ||
130 | 131 | ||
131 | try { | 132 | try { |
132 | $entries->setCurrentPage($page); | 133 | $entries->setCurrentPage($page); |
@@ -143,7 +144,7 @@ class TagController extends Controller | |||
143 | 'form' => null, | 144 | 'form' => null, |
144 | 'entries' => $entries, | 145 | 'entries' => $entries, |
145 | 'currentPage' => $page, | 146 | 'currentPage' => $page, |
146 | 'tag' => $tag->getSlug(), | 147 | 'tag' => $tag, |
147 | ]); | 148 | ]); |
148 | } | 149 | } |
149 | } | 150 | } |
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php index a723656e..a52288e6 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php | |||
@@ -6,163 +6,27 @@ use Doctrine\Common\DataFixtures\AbstractFixture; | |||
6 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; | 6 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; |
7 | use Doctrine\Common\Persistence\ObjectManager; | 7 | use Doctrine\Common\Persistence\ObjectManager; |
8 | use Craue\ConfigBundle\Entity\Setting; | 8 | use Craue\ConfigBundle\Entity\Setting; |
9 | use Symfony\Component\DependencyInjection\ContainerAwareInterface; | ||
10 | use Symfony\Component\DependencyInjection\ContainerInterface; | ||
9 | 11 | ||
10 | class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface | 12 | class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface |
11 | { | 13 | { |
12 | /** | 14 | /** |
15 | * @var ContainerInterface | ||
16 | */ | ||
17 | private $container; | ||
18 | |||
19 | public function setContainer(ContainerInterface $container = null) | ||
20 | { | ||
21 | $this->container = $container; | ||
22 | } | ||
23 | |||
24 | /** | ||
13 | * {@inheritdoc} | 25 | * {@inheritdoc} |
14 | */ | 26 | */ |
15 | public function load(ObjectManager $manager) | 27 | public function load(ObjectManager $manager) |
16 | { | 28 | { |
17 | $settings = [ | 29 | foreach ($this->container->getParameter('wallabag_core.default_internal_settings') as $setting) { |
18 | [ | ||
19 | 'name' => 'share_public', | ||
20 | 'value' => '1', | ||
21 | 'section' => 'entry', | ||
22 | ], | ||
23 | [ | ||
24 | 'name' => 'carrot', | ||
25 | 'value' => '1', | ||
26 | 'section' => 'entry', | ||
27 | ], | ||
28 | [ | ||
29 | 'name' => 'share_diaspora', | ||
30 | 'value' => '1', | ||
31 | 'section' => 'entry', | ||
32 | ], | ||
33 | [ | ||
34 | 'name' => 'diaspora_url', | ||
35 | 'value' => 'http://diasporapod.com', | ||
36 | 'section' => 'entry', | ||
37 | ], | ||
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 | [ | ||
49 | 'name' => 'share_shaarli', | ||
50 | 'value' => '1', | ||
51 | 'section' => 'entry', | ||
52 | ], | ||
53 | [ | ||
54 | 'name' => 'shaarli_url', | ||
55 | 'value' => 'http://myshaarli.com', | ||
56 | 'section' => 'entry', | ||
57 | ], | ||
58 | [ | ||
59 | 'name' => 'share_mail', | ||
60 | 'value' => '1', | ||
61 | 'section' => 'entry', | ||
62 | ], | ||
63 | [ | ||
64 | 'name' => 'share_twitter', | ||
65 | 'value' => '1', | ||
66 | 'section' => 'entry', | ||
67 | ], | ||
68 | [ | ||
69 | 'name' => 'export_epub', | ||
70 | 'value' => '1', | ||
71 | 'section' => 'export', | ||
72 | ], | ||
73 | [ | ||
74 | 'name' => 'export_mobi', | ||
75 | 'value' => '1', | ||
76 | 'section' => 'export', | ||
77 | ], | ||
78 | [ | ||
79 | 'name' => 'export_pdf', | ||
80 | 'value' => '1', | ||
81 | 'section' => 'export', | ||
82 | ], | ||
83 | [ | ||
84 | 'name' => 'export_csv', | ||
85 | 'value' => '1', | ||
86 | 'section' => 'export', | ||
87 | ], | ||
88 | [ | ||
89 | 'name' => 'export_json', | ||
90 | 'value' => '1', | ||
91 | 'section' => 'export', | ||
92 | ], | ||
93 | [ | ||
94 | 'name' => 'export_txt', | ||
95 | 'value' => '1', | ||
96 | 'section' => 'export', | ||
97 | ], | ||
98 | [ | ||
99 | 'name' => 'export_xml', | ||
100 | 'value' => '1', | ||
101 | 'section' => 'export', | ||
102 | ], | ||
103 | [ | ||
104 | 'name' => 'import_with_redis', | ||
105 | 'value' => '0', | ||
106 | 'section' => 'import', | ||
107 | ], | ||
108 | [ | ||
109 | 'name' => 'import_with_rabbitmq', | ||
110 | 'value' => '0', | ||
111 | 'section' => 'import', | ||
112 | ], | ||
113 | [ | ||
114 | 'name' => 'show_printlink', | ||
115 | 'value' => '1', | ||
116 | 'section' => 'entry', | ||
117 | ], | ||
118 | [ | ||
119 | 'name' => 'wallabag_support_url', | ||
120 | 'value' => 'https://www.wallabag.org/pages/support.html', | ||
121 | 'section' => 'misc', | ||
122 | ], | ||
123 | [ | ||
124 | 'name' => 'wallabag_url', | ||
125 | 'value' => 'http://v2.wallabag.org', | ||
126 | 'section' => 'misc', | ||
127 | ], | ||
128 | [ | ||
129 | 'name' => 'piwik_enabled', | ||
130 | 'value' => '0', | ||
131 | 'section' => 'analytics', | ||
132 | ], | ||
133 | [ | ||
134 | 'name' => 'piwik_host', | ||
135 | 'value' => 'v2.wallabag.org', | ||
136 | 'section' => 'analytics', | ||
137 | ], | ||
138 | [ | ||
139 | 'name' => 'piwik_site_id', | ||
140 | 'value' => '1', | ||
141 | 'section' => 'analytics', | ||
142 | ], | ||
143 | [ | ||
144 | 'name' => 'demo_mode_enabled', | ||
145 | 'value' => '0', | ||
146 | 'section' => 'misc', | ||
147 | ], | ||
148 | [ | ||
149 | 'name' => 'demo_mode_username', | ||
150 | 'value' => 'wallabag', | ||
151 | 'section' => 'misc', | ||
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 | ], | ||
163 | ]; | ||
164 | |||
165 | foreach ($settings as $setting) { | ||
166 | $newSetting = new Setting(); | 30 | $newSetting = new Setting(); |
167 | $newSetting->setName($setting['name']); | 31 | $newSetting->setName($setting['name']); |
168 | $newSetting->setValue($setting['value']); | 32 | $newSetting->setValue($setting['value']); |
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSiteCredentialData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSiteCredentialData.php new file mode 100644 index 00000000..866f55a4 --- /dev/null +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSiteCredentialData.php | |||
@@ -0,0 +1,34 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\DataFixtures\ORM; | ||
4 | |||
5 | use Doctrine\Common\DataFixtures\AbstractFixture; | ||
6 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; | ||
7 | use Doctrine\Common\Persistence\ObjectManager; | ||
8 | use Wallabag\CoreBundle\Entity\SiteCredential; | ||
9 | |||
10 | class LoadSiteCredentialData extends AbstractFixture implements OrderedFixtureInterface | ||
11 | { | ||
12 | /** | ||
13 | * {@inheritdoc} | ||
14 | */ | ||
15 | public function load(ObjectManager $manager) | ||
16 | { | ||
17 | $credential = new SiteCredential($this->getReference('admin-user')); | ||
18 | $credential->setHost('example.com'); | ||
19 | $credential->setUsername('foo'); | ||
20 | $credential->setPassword('bar'); | ||
21 | |||
22 | $manager->persist($credential); | ||
23 | |||
24 | $manager->flush(); | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * {@inheritdoc} | ||
29 | */ | ||
30 | public function getOrder() | ||
31 | { | ||
32 | return 50; | ||
33 | } | ||
34 | } | ||
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php index 7efe6356..55abd63c 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php | |||
@@ -36,6 +36,13 @@ class LoadTaggingRuleData extends AbstractFixture implements OrderedFixtureInter | |||
36 | 36 | ||
37 | $manager->persist($tr3); | 37 | $manager->persist($tr3); |
38 | 38 | ||
39 | $tr4 = new TaggingRule(); | ||
40 | $tr4->setRule('content notmatches "basket"'); | ||
41 | $tr4->setTags(['foot']); | ||
42 | $tr4->setConfig($this->getReference('admin-config')); | ||
43 | |||
44 | $manager->persist($tr4); | ||
45 | |||
39 | $manager->flush(); | 46 | $manager->flush(); |
40 | } | 47 | } |
41 | 48 | ||
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php index 006a18c3..a9791f6b 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php | |||
@@ -41,12 +41,30 @@ class Configuration implements ConfigurationInterface | |||
41 | ->end() | 41 | ->end() |
42 | ->scalarNode('fetching_error_message') | 42 | ->scalarNode('fetching_error_message') |
43 | ->end() | 43 | ->end() |
44 | ->scalarNode('fetching_error_message_title') | ||
45 | ->end() | ||
44 | ->scalarNode('action_mark_as_read') | 46 | ->scalarNode('action_mark_as_read') |
45 | ->defaultValue(1) | 47 | ->defaultValue(1) |
46 | ->end() | 48 | ->end() |
47 | ->scalarNode('list_mode') | 49 | ->scalarNode('list_mode') |
48 | ->defaultValue(1) | 50 | ->defaultValue(1) |
49 | ->end() | 51 | ->end() |
52 | ->scalarNode('api_limit_mass_actions') | ||
53 | ->defaultValue(10) | ||
54 | ->end() | ||
55 | ->arrayNode('default_internal_settings') | ||
56 | ->prototype('array') | ||
57 | ->children() | ||
58 | ->scalarNode('name')->end() | ||
59 | ->scalarNode('value')->end() | ||
60 | ->enumNode('section') | ||
61 | ->values(['entry', 'misc', 'api', 'analytics', 'export', 'import']) | ||
62 | ->end() | ||
63 | ->end() | ||
64 | ->end() | ||
65 | ->end() | ||
66 | ->scalarNode('encryption_key_path') | ||
67 | ->end() | ||
50 | ->end() | 68 | ->end() |
51 | ; | 69 | ; |
52 | 70 | ||
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php index aa9ee339..532ce238 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php | |||
@@ -26,6 +26,10 @@ class WallabagCoreExtension extends Extension | |||
26 | $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']); | 26 | $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']); |
27 | $container->setParameter('wallabag_core.list_mode', $config['list_mode']); | 27 | $container->setParameter('wallabag_core.list_mode', $config['list_mode']); |
28 | $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); | 28 | $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); |
29 | $container->setParameter('wallabag_core.fetching_error_message_title', $config['fetching_error_message_title']); | ||
30 | $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']); | ||
31 | $container->setParameter('wallabag_core.default_internal_settings', $config['default_internal_settings']); | ||
32 | $container->setParameter('wallabag_core.site_credentials.encryption_key_path', $config['encryption_key_path']); | ||
29 | 33 | ||
30 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); | 34 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
31 | $loader->load('services.yml'); | 35 | $loader->load('services.yml'); |
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 7276b437..a0503c39 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php | |||
@@ -122,6 +122,24 @@ class Entry | |||
122 | private $updatedAt; | 122 | private $updatedAt; |
123 | 123 | ||
124 | /** | 124 | /** |
125 | * @var \DateTime | ||
126 | * | ||
127 | * @ORM\Column(name="published_at", type="datetime", nullable=true) | ||
128 | * | ||
129 | * @Groups({"entries_for_user", "export_all"}) | ||
130 | */ | ||
131 | private $publishedAt; | ||
132 | |||
133 | /** | ||
134 | * @var array | ||
135 | * | ||
136 | * @ORM\Column(name="published_by", type="array", nullable=true) | ||
137 | * | ||
138 | * @Groups({"entries_for_user", "export_all"}) | ||
139 | */ | ||
140 | private $publishedBy; | ||
141 | |||
142 | /** | ||
125 | * @ORM\OneToMany(targetEntity="Wallabag\AnnotationBundle\Entity\Annotation", mappedBy="entry", cascade={"persist", "remove"}) | 143 | * @ORM\OneToMany(targetEntity="Wallabag\AnnotationBundle\Entity\Annotation", mappedBy="entry", cascade={"persist", "remove"}) |
126 | * @ORM\JoinTable | 144 | * @ORM\JoinTable |
127 | * | 145 | * |
@@ -175,22 +193,22 @@ class Entry | |||
175 | private $previewPicture; | 193 | private $previewPicture; |
176 | 194 | ||
177 | /** | 195 | /** |
178 | * @var bool | 196 | * @var string |
179 | * | 197 | * |
180 | * @ORM\Column(name="is_public", type="boolean", nullable=true, options={"default" = false}) | 198 | * @ORM\Column(name="http_status", type="string", length=3, nullable=true) |
181 | * | 199 | * |
182 | * @Groups({"export_all"}) | 200 | * @Groups({"entries_for_user", "export_all"}) |
183 | */ | 201 | */ |
184 | private $isPublic; | 202 | private $httpStatus; |
185 | 203 | ||
186 | /** | 204 | /** |
187 | * @var string | 205 | * @var array |
188 | * | 206 | * |
189 | * @ORM\Column(name="http_status", type="string", length=3, nullable=true) | 207 | * @ORM\Column(name="headers", type="array", nullable=true) |
190 | * | 208 | * |
191 | * @Groups({"entries_for_user", "export_all"}) | 209 | * @Groups({"entries_for_user", "export_all"}) |
192 | */ | 210 | */ |
193 | private $httpStatus; | 211 | private $headers; |
194 | 212 | ||
195 | /** | 213 | /** |
196 | * @Exclude | 214 | * @Exclude |
@@ -532,23 +550,7 @@ class Entry | |||
532 | } | 550 | } |
533 | 551 | ||
534 | /** | 552 | /** |
535 | * @return bool | 553 | * @return ArrayCollection |
536 | */ | ||
537 | public function isPublic() | ||
538 | { | ||
539 | return $this->isPublic; | ||
540 | } | ||
541 | |||
542 | /** | ||
543 | * @param bool $isPublic | ||
544 | */ | ||
545 | public function setIsPublic($isPublic) | ||
546 | { | ||
547 | $this->isPublic = $isPublic; | ||
548 | } | ||
549 | |||
550 | /** | ||
551 | * @return ArrayCollection<Tag> | ||
552 | */ | 554 | */ |
553 | public function getTags() | 555 | public function getTags() |
554 | { | 556 | { |
@@ -683,7 +685,22 @@ class Entry | |||
683 | } | 685 | } |
684 | 686 | ||
685 | /** | 687 | /** |
686 | * @return int | 688 | * Used in the entries filter so it's more explicit for the end user than the uid. |
689 | * Also used in the API. | ||
690 | * | ||
691 | * @VirtualProperty | ||
692 | * @SerializedName("is_public") | ||
693 | * @Groups({"entries_for_user"}) | ||
694 | * | ||
695 | * @return bool | ||
696 | */ | ||
697 | public function isPublic() | ||
698 | { | ||
699 | return null !== $this->uid; | ||
700 | } | ||
701 | |||
702 | /** | ||
703 | * @return string | ||
687 | */ | 704 | */ |
688 | public function getHttpStatus() | 705 | public function getHttpStatus() |
689 | { | 706 | { |
@@ -691,7 +708,7 @@ class Entry | |||
691 | } | 708 | } |
692 | 709 | ||
693 | /** | 710 | /** |
694 | * @param int $httpStatus | 711 | * @param string $httpStatus |
695 | * | 712 | * |
696 | * @return Entry | 713 | * @return Entry |
697 | */ | 714 | */ |
@@ -701,4 +718,64 @@ class Entry | |||
701 | 718 | ||
702 | return $this; | 719 | return $this; |
703 | } | 720 | } |
721 | |||
722 | /** | ||
723 | * @return \Datetime | ||
724 | */ | ||
725 | public function getPublishedAt() | ||
726 | { | ||
727 | return $this->publishedAt; | ||
728 | } | ||
729 | |||
730 | /** | ||
731 | * @param \Datetime $publishedAt | ||
732 | * | ||
733 | * @return Entry | ||
734 | */ | ||
735 | public function setPublishedAt(\Datetime $publishedAt) | ||
736 | { | ||
737 | $this->publishedAt = $publishedAt; | ||
738 | |||
739 | return $this; | ||
740 | } | ||
741 | |||
742 | /** | ||
743 | * @return array | ||
744 | */ | ||
745 | public function getPublishedBy() | ||
746 | { | ||
747 | return $this->publishedBy; | ||
748 | } | ||
749 | |||
750 | /** | ||
751 | * @param array $publishedBy | ||
752 | * | ||
753 | * @return Entry | ||
754 | */ | ||
755 | public function setPublishedBy($publishedBy) | ||
756 | { | ||
757 | $this->publishedBy = $publishedBy; | ||
758 | |||
759 | return $this; | ||
760 | } | ||
761 | |||
762 | /** | ||
763 | * @return array | ||
764 | */ | ||
765 | public function getHeaders() | ||
766 | { | ||
767 | return $this->headers; | ||
768 | } | ||
769 | |||
770 | /** | ||
771 | * @param array $headers | ||
772 | * | ||
773 | * @return Entry | ||
774 | */ | ||
775 | public function setHeaders($headers) | ||
776 | { | ||
777 | $this->headers = $headers; | ||
778 | |||
779 | return $this; | ||
780 | } | ||
704 | } | 781 | } |
diff --git a/src/Wallabag/CoreBundle/Entity/SiteCredential.php b/src/Wallabag/CoreBundle/Entity/SiteCredential.php new file mode 100644 index 00000000..58075e92 --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/SiteCredential.php | |||
@@ -0,0 +1,195 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Entity; | ||
4 | |||
5 | use Doctrine\ORM\Mapping as ORM; | ||
6 | use Symfony\Component\Validator\Constraints as Assert; | ||
7 | use Wallabag\UserBundle\Entity\User; | ||
8 | |||
9 | /** | ||
10 | * SiteCredential. | ||
11 | * | ||
12 | * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\SiteCredentialRepository") | ||
13 | * @ORM\Table(name="`site_credential`") | ||
14 | * @ORM\HasLifecycleCallbacks() | ||
15 | */ | ||
16 | class SiteCredential | ||
17 | { | ||
18 | /** | ||
19 | * @var int | ||
20 | * | ||
21 | * @ORM\Column(name="id", type="integer") | ||
22 | * @ORM\Id | ||
23 | * @ORM\GeneratedValue(strategy="AUTO") | ||
24 | */ | ||
25 | private $id; | ||
26 | |||
27 | /** | ||
28 | * @var string | ||
29 | * | ||
30 | * @Assert\NotBlank() | ||
31 | * @Assert\Length(max=255) | ||
32 | * @ORM\Column(name="host", type="string", length=255) | ||
33 | */ | ||
34 | private $host; | ||
35 | |||
36 | /** | ||
37 | * @var string | ||
38 | * | ||
39 | * @Assert\NotBlank() | ||
40 | * @ORM\Column(name="username", type="text") | ||
41 | */ | ||
42 | private $username; | ||
43 | |||
44 | /** | ||
45 | * @var string | ||
46 | * | ||
47 | * @Assert\NotBlank() | ||
48 | * @ORM\Column(name="password", type="text") | ||
49 | */ | ||
50 | private $password; | ||
51 | |||
52 | /** | ||
53 | * @var \DateTime | ||
54 | * | ||
55 | * @ORM\Column(name="createdAt", type="datetime") | ||
56 | */ | ||
57 | private $createdAt; | ||
58 | |||
59 | /** | ||
60 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="site_credentials") | ||
61 | */ | ||
62 | private $user; | ||
63 | |||
64 | /* | ||
65 | * @param User $user | ||
66 | */ | ||
67 | public function __construct(User $user) | ||
68 | { | ||
69 | $this->user = $user; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Get id. | ||
74 | * | ||
75 | * @return int | ||
76 | */ | ||
77 | public function getId() | ||
78 | { | ||
79 | return $this->id; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Set host. | ||
84 | * | ||
85 | * @param string $host | ||
86 | * | ||
87 | * @return SiteCredential | ||
88 | */ | ||
89 | public function setHost($host) | ||
90 | { | ||
91 | $this->host = $host; | ||
92 | |||
93 | return $this; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Get host. | ||
98 | * | ||
99 | * @return string | ||
100 | */ | ||
101 | public function getHost() | ||
102 | { | ||
103 | return $this->host; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Set username. | ||
108 | * | ||
109 | * @param string $username | ||
110 | * | ||
111 | * @return SiteCredential | ||
112 | */ | ||
113 | public function setUsername($username) | ||
114 | { | ||
115 | $this->username = $username; | ||
116 | |||
117 | return $this; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * Get username. | ||
122 | * | ||
123 | * @return string | ||
124 | */ | ||
125 | public function getUsername() | ||
126 | { | ||
127 | return $this->username; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Set password. | ||
132 | * | ||
133 | * @param string $password | ||
134 | * | ||
135 | * @return SiteCredential | ||
136 | */ | ||
137 | public function setPassword($password) | ||
138 | { | ||
139 | $this->password = $password; | ||
140 | |||
141 | return $this; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * Get password. | ||
146 | * | ||
147 | * @return string | ||
148 | */ | ||
149 | public function getPassword() | ||
150 | { | ||
151 | return $this->password; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * Set createdAt. | ||
156 | * | ||
157 | * @param \DateTime $createdAt | ||
158 | * | ||
159 | * @return SiteCredential | ||
160 | */ | ||
161 | public function setCreatedAt($createdAt) | ||
162 | { | ||
163 | $this->createdAt = $createdAt; | ||
164 | |||
165 | return $this; | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * Get createdAt. | ||
170 | * | ||
171 | * @return \DateTime | ||
172 | */ | ||
173 | public function getCreatedAt() | ||
174 | { | ||
175 | return $this->createdAt; | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * @return User | ||
180 | */ | ||
181 | public function getUser() | ||
182 | { | ||
183 | return $this->user; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @ORM\PrePersist | ||
188 | */ | ||
189 | public function timestamps() | ||
190 | { | ||
191 | if (is_null($this->createdAt)) { | ||
192 | $this->createdAt = new \DateTime(); | ||
193 | } | ||
194 | } | ||
195 | } | ||
diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 72651b19..84e11e26 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php | |||
@@ -31,7 +31,7 @@ class TaggingRule | |||
31 | * @Assert\Length(max=255) | 31 | * @Assert\Length(max=255) |
32 | * @RulerZAssert\ValidRule( | 32 | * @RulerZAssert\ValidRule( |
33 | * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, | 33 | * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, |
34 | * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"} | 34 | * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches", "notmatches"} |
35 | * ) | 35 | * ) |
36 | * @ORM\Column(name="rule", type="string", nullable=false) | 36 | * @ORM\Column(name="rule", type="string", nullable=false) |
37 | */ | 37 | */ |
@@ -87,7 +87,7 @@ class TaggingRule | |||
87 | /** | 87 | /** |
88 | * Set tags. | 88 | * Set tags. |
89 | * | 89 | * |
90 | * @param array<string> $tags | 90 | * @param array <string> $tags |
91 | * | 91 | * |
92 | * @return TaggingRule | 92 | * @return TaggingRule |
93 | */ | 93 | */ |
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php index 3b4c4cf9..5e6af8cc 100644 --- a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php +++ b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php | |||
@@ -45,9 +45,8 @@ class SQLiteCascadeDeleteSubscriber implements EventSubscriber | |||
45 | public function preRemove(LifecycleEventArgs $args) | 45 | public function preRemove(LifecycleEventArgs $args) |
46 | { | 46 | { |
47 | $entity = $args->getEntity(); | 47 | $entity = $args->getEntity(); |
48 | 48 | if (!$this->doctrine->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform | |
49 | if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver || | 49 | || !$entity instanceof Entry) { |
50 | !$entity instanceof Entry) { | ||
51 | return; | 50 | return; |
52 | } | 51 | } |
53 | 52 | ||
diff --git a/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php b/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php index c3715646..1627cc44 100644 --- a/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php +++ b/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php | |||
@@ -3,7 +3,6 @@ | |||
3 | namespace Wallabag\CoreBundle\Form\Type; | 3 | namespace Wallabag\CoreBundle\Form\Type; |
4 | 4 | ||
5 | use Symfony\Component\Form\AbstractType; | 5 | use Symfony\Component\Form\AbstractType; |
6 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | ||
7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | 6 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; |
8 | use Symfony\Component\Form\Extension\Core\Type\TextType; | 7 | use Symfony\Component\Form\Extension\Core\Type\TextType; |
9 | use Symfony\Component\Form\FormBuilderInterface; | 8 | use Symfony\Component\Form\FormBuilderInterface; |
@@ -18,11 +17,6 @@ class EditEntryType extends AbstractType | |||
18 | 'required' => true, | 17 | 'required' => true, |
19 | 'label' => 'entry.edit.title_label', | 18 | 'label' => 'entry.edit.title_label', |
20 | ]) | 19 | ]) |
21 | ->add('is_public', CheckboxType::class, [ | ||
22 | 'required' => false, | ||
23 | 'label' => 'entry.edit.is_public_label', | ||
24 | 'property_path' => 'isPublic', | ||
25 | ]) | ||
26 | ->add('url', TextType::class, [ | 20 | ->add('url', TextType::class, [ |
27 | 'disabled' => true, | 21 | 'disabled' => true, |
28 | 'required' => false, | 22 | 'required' => false, |
diff --git a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php index 556578d1..6a4c485f 100644 --- a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php +++ b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php | |||
@@ -150,6 +150,20 @@ class EntryFilterType extends AbstractType | |||
150 | }, | 150 | }, |
151 | 'label' => 'entry.filters.preview_picture_label', | 151 | 'label' => 'entry.filters.preview_picture_label', |
152 | ]) | 152 | ]) |
153 | ->add('isPublic', CheckboxFilterType::class, [ | ||
154 | 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { | ||
155 | if (false === $values['value']) { | ||
156 | return; | ||
157 | } | ||
158 | |||
159 | // is_public isn't a real field | ||
160 | // we should use the "uid" field to determine if the entry has been made public | ||
161 | $expression = $filterQuery->getExpr()->isNotNull($values['alias'].'.uid'); | ||
162 | |||
163 | return $filterQuery->createCondition($expression); | ||
164 | }, | ||
165 | 'label' => 'entry.filters.is_public_label', | ||
166 | ]) | ||
153 | ->add('language', ChoiceFilterType::class, [ | 167 | ->add('language', ChoiceFilterType::class, [ |
154 | 'choices' => array_flip($this->repository->findDistinctLanguageByUser($this->user->getId())), | 168 | 'choices' => array_flip($this->repository->findDistinctLanguageByUser($this->user->getId())), |
155 | 'label' => 'entry.filters.language_label', | 169 | 'label' => 'entry.filters.language_label', |
diff --git a/src/Wallabag/CoreBundle/Form/Type/SiteCredentialType.php b/src/Wallabag/CoreBundle/Form/Type/SiteCredentialType.php new file mode 100644 index 00000000..fd409ad2 --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/Type/SiteCredentialType.php | |||
@@ -0,0 +1,44 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Form\Type; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\Extension\Core\Type\PasswordType; | ||
7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | ||
8 | use Symfony\Component\Form\Extension\Core\Type\TextType; | ||
9 | use Symfony\Component\Form\FormBuilderInterface; | ||
10 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
11 | |||
12 | class SiteCredentialType extends AbstractType | ||
13 | { | ||
14 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
15 | { | ||
16 | $builder | ||
17 | ->add('host', TextType::class, [ | ||
18 | 'label' => 'site_credential.form.host_label', | ||
19 | ]) | ||
20 | ->add('username', TextType::class, [ | ||
21 | 'label' => 'site_credential.form.username_label', | ||
22 | 'data' => '', | ||
23 | ]) | ||
24 | ->add('password', PasswordType::class, [ | ||
25 | 'label' => 'site_credential.form.password_label', | ||
26 | ]) | ||
27 | ->add('save', SubmitType::class, [ | ||
28 | 'label' => 'config.form.save', | ||
29 | ]) | ||
30 | ; | ||
31 | } | ||
32 | |||
33 | public function configureOptions(OptionsResolver $resolver) | ||
34 | { | ||
35 | $resolver->setDefaults([ | ||
36 | 'data_class' => 'Wallabag\CoreBundle\Entity\SiteCredential', | ||
37 | ]); | ||
38 | } | ||
39 | |||
40 | public function getBlockPrefix() | ||
41 | { | ||
42 | return 'site_credential'; | ||
43 | } | ||
44 | } | ||
diff --git a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php index 6d4129e8..a79e6ebe 100644 --- a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php +++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php | |||
@@ -5,39 +5,53 @@ namespace Wallabag\CoreBundle\GuzzleSiteAuthenticator; | |||
5 | use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig; | 5 | use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig; |
6 | use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfigBuilder; | 6 | use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfigBuilder; |
7 | use Graby\SiteConfig\ConfigBuilder; | 7 | use Graby\SiteConfig\ConfigBuilder; |
8 | use OutOfRangeException; | 8 | use Psr\Log\LoggerInterface; |
9 | use Wallabag\CoreBundle\Repository\SiteCredentialRepository; | ||
10 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; | ||
9 | 11 | ||
10 | class GrabySiteConfigBuilder implements SiteConfigBuilder | 12 | class GrabySiteConfigBuilder implements SiteConfigBuilder |
11 | { | 13 | { |
12 | /** | 14 | /** |
13 | * @var \Graby\SiteConfig\ConfigBuilder | 15 | * @var ConfigBuilder |
14 | */ | 16 | */ |
15 | private $grabyConfigBuilder; | 17 | private $grabyConfigBuilder; |
18 | |||
19 | /** | ||
20 | * @var SiteCredentialRepository | ||
21 | */ | ||
22 | private $credentialRepository; | ||
23 | |||
16 | /** | 24 | /** |
17 | * @var array | 25 | * @var LoggerInterface |
18 | */ | 26 | */ |
19 | private $credentials; | 27 | private $logger; |
28 | |||
29 | /** | ||
30 | * @var Wallabag\UserBundle\Entity\User|null | ||
31 | */ | ||
32 | private $currentUser; | ||
20 | 33 | ||
21 | /** | 34 | /** |
22 | * GrabySiteConfigBuilder constructor. | 35 | * GrabySiteConfigBuilder constructor. |
23 | * | 36 | * |
24 | * @param \Graby\SiteConfig\ConfigBuilder $grabyConfigBuilder | 37 | * @param ConfigBuilder $grabyConfigBuilder |
25 | * @param array $credentials | 38 | * @param TokenStorage $token |
39 | * @param SiteCredentialRepository $credentialRepository | ||
40 | * @param LoggerInterface $logger | ||
26 | */ | 41 | */ |
27 | public function __construct(ConfigBuilder $grabyConfigBuilder, array $credentials = []) | 42 | public function __construct(ConfigBuilder $grabyConfigBuilder, TokenStorage $token, SiteCredentialRepository $credentialRepository, LoggerInterface $logger) |
28 | { | 43 | { |
29 | $this->grabyConfigBuilder = $grabyConfigBuilder; | 44 | $this->grabyConfigBuilder = $grabyConfigBuilder; |
30 | $this->credentials = $credentials; | 45 | $this->credentialRepository = $credentialRepository; |
46 | $this->logger = $logger; | ||
47 | |||
48 | if ($token->getToken()) { | ||
49 | $this->currentUser = $token->getToken()->getUser(); | ||
50 | } | ||
31 | } | 51 | } |
32 | 52 | ||
33 | /** | 53 | /** |
34 | * Builds the SiteConfig for a host. | 54 | * {@inheritdoc} |
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 | */ | 55 | */ |
42 | public function buildForHost($host) | 56 | public function buildForHost($host) |
43 | { | 57 | { |
@@ -47,6 +61,17 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder | |||
47 | $host = substr($host, 4); | 61 | $host = substr($host, 4); |
48 | } | 62 | } |
49 | 63 | ||
64 | $credentials = null; | ||
65 | if ($this->currentUser) { | ||
66 | $credentials = $this->credentialRepository->findOneByHostAndUser($host, $this->currentUser->getId()); | ||
67 | } | ||
68 | |||
69 | if (null === $credentials) { | ||
70 | $this->logger->debug('Auth: no credentials available for host.', ['host' => $host]); | ||
71 | |||
72 | return false; | ||
73 | } | ||
74 | |||
50 | $config = $this->grabyConfigBuilder->buildForHost($host); | 75 | $config = $this->grabyConfigBuilder->buildForHost($host); |
51 | $parameters = [ | 76 | $parameters = [ |
52 | 'host' => $host, | 77 | 'host' => $host, |
@@ -54,15 +79,47 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder | |||
54 | 'loginUri' => $config->login_uri ?: null, | 79 | 'loginUri' => $config->login_uri ?: null, |
55 | 'usernameField' => $config->login_username_field ?: null, | 80 | 'usernameField' => $config->login_username_field ?: null, |
56 | 'passwordField' => $config->login_password_field ?: null, | 81 | 'passwordField' => $config->login_password_field ?: null, |
57 | 'extraFields' => is_array($config->login_extra_fields) ? $config->login_extra_fields : [], | 82 | 'extraFields' => $this->processExtraFields($config->login_extra_fields), |
58 | 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null, | 83 | 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null, |
84 | 'username' => $credentials['username'], | ||
85 | 'password' => $credentials['password'], | ||
59 | ]; | 86 | ]; |
60 | 87 | ||
61 | if (isset($this->credentials[$host])) { | 88 | $config = new SiteConfig($parameters); |
62 | $parameters['username'] = $this->credentials[$host]['username']; | 89 | |
63 | $parameters['password'] = $this->credentials[$host]['password']; | 90 | // do not leak usernames and passwords in log |
91 | $parameters['username'] = '**masked**'; | ||
92 | $parameters['password'] = '**masked**'; | ||
93 | |||
94 | $this->logger->debug('Auth: add parameters.', ['host' => $host, 'parameters' => $parameters]); | ||
95 | |||
96 | return $config; | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Processes login_extra_fields config, transforming an '=' separated array of strings | ||
101 | * into a key/value array. | ||
102 | * | ||
103 | * @param array|mixed $extraFieldsStrings | ||
104 | * | ||
105 | * @return array | ||
106 | */ | ||
107 | protected function processExtraFields($extraFieldsStrings) | ||
108 | { | ||
109 | if (!is_array($extraFieldsStrings)) { | ||
110 | return []; | ||
111 | } | ||
112 | |||
113 | $extraFields = []; | ||
114 | foreach ($extraFieldsStrings as $extraField) { | ||
115 | if (strpos($extraField, '=') === false) { | ||
116 | continue; | ||
117 | } | ||
118 | |||
119 | list($fieldName, $fieldValue) = explode('=', $extraField, 2); | ||
120 | $extraFields[$fieldName] = $fieldValue; | ||
64 | } | 121 | } |
65 | 122 | ||
66 | return new SiteConfig($parameters); | 123 | return $extraFields; |
67 | } | 124 | } |
68 | } | 125 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index f222dd88..51bb2ca2 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php | |||
@@ -5,10 +5,11 @@ namespace Wallabag\CoreBundle\Helper; | |||
5 | use Graby\Graby; | 5 | use Graby\Graby; |
6 | use Psr\Log\LoggerInterface; | 6 | use Psr\Log\LoggerInterface; |
7 | use Wallabag\CoreBundle\Entity\Entry; | 7 | use Wallabag\CoreBundle\Entity\Entry; |
8 | use Wallabag\CoreBundle\Entity\Tag; | ||
9 | use Wallabag\CoreBundle\Tools\Utils; | 8 | use Wallabag\CoreBundle\Tools\Utils; |
10 | use Wallabag\CoreBundle\Repository\TagRepository; | ||
11 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; | 9 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; |
10 | use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint; | ||
11 | use Symfony\Component\Validator\Constraints\Url as UrlConstraint; | ||
12 | use Symfony\Component\Validator\Validator\ValidatorInterface; | ||
12 | 13 | ||
13 | /** | 14 | /** |
14 | * This kind of proxy class take care of getting the content from an url | 15 | * This kind of proxy class take care of getting the content from an url |
@@ -18,38 +19,37 @@ class ContentProxy | |||
18 | { | 19 | { |
19 | protected $graby; | 20 | protected $graby; |
20 | protected $tagger; | 21 | protected $tagger; |
22 | protected $validator; | ||
21 | protected $logger; | 23 | protected $logger; |
22 | protected $tagRepository; | ||
23 | protected $mimeGuesser; | 24 | protected $mimeGuesser; |
24 | protected $fetchingErrorMessage; | 25 | protected $fetchingErrorMessage; |
26 | protected $eventDispatcher; | ||
25 | 27 | ||
26 | public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, LoggerInterface $logger, $fetchingErrorMessage) | 28 | public function __construct(Graby $graby, RuleBasedTagger $tagger, ValidatorInterface $validator, LoggerInterface $logger, $fetchingErrorMessage) |
27 | { | 29 | { |
28 | $this->graby = $graby; | 30 | $this->graby = $graby; |
29 | $this->tagger = $tagger; | 31 | $this->tagger = $tagger; |
32 | $this->validator = $validator; | ||
30 | $this->logger = $logger; | 33 | $this->logger = $logger; |
31 | $this->tagRepository = $tagRepository; | ||
32 | $this->mimeGuesser = new MimeTypeExtensionGuesser(); | 34 | $this->mimeGuesser = new MimeTypeExtensionGuesser(); |
33 | $this->fetchingErrorMessage = $fetchingErrorMessage; | 35 | $this->fetchingErrorMessage = $fetchingErrorMessage; |
34 | } | 36 | } |
35 | 37 | ||
36 | /** | 38 | /** |
37 | * Fetch content using graby and hydrate given entry with results information. | 39 | * Update entry using either fetched or provided content. |
38 | * In case we couldn't find content, we'll try to use Open Graph data. | ||
39 | * | 40 | * |
40 | * We can also force the content, in case of an import from the v1 for example, so the function won't | 41 | * @param Entry $entry Entry to update |
41 | * fetch the content from the website but rather use information given with the $content parameter. | 42 | * @param string $url Url of the content |
42 | * | 43 | * @param array $content Array with content provided for import with AT LEAST keys title, html, url to skip the fetchContent from the url |
43 | * @param Entry $entry Entry to update | 44 | * @param bool $disableContentUpdate Whether to skip trying to fetch content using Graby |
44 | * @param string $url Url to grab content for | ||
45 | * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url | ||
46 | * | ||
47 | * @return Entry | ||
48 | */ | 45 | */ |
49 | public function updateEntry(Entry $entry, $url, array $content = []) | 46 | public function updateEntry(Entry $entry, $url, array $content = [], $disableContentUpdate = false) |
50 | { | 47 | { |
51 | // do we have to fetch the content or the provided one is ok? | 48 | if (!empty($content['html'])) { |
52 | if (empty($content) || false === $this->validateContent($content)) { | 49 | $content['html'] = $this->graby->cleanupHtml($content['html'], $url); |
50 | } | ||
51 | |||
52 | if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) { | ||
53 | $fetchedContent = $this->graby->fetchContent($url); | 53 | $fetchedContent = $this->graby->fetchContent($url); |
54 | 54 | ||
55 | // when content is imported, we have information in $content | 55 | // when content is imported, we have information in $content |
@@ -59,8 +59,24 @@ class ContentProxy | |||
59 | } | 59 | } |
60 | } | 60 | } |
61 | 61 | ||
62 | // be sure to keep the url in case of error | ||
63 | // so we'll be able to refetch it in the future | ||
64 | $content['url'] = !empty($content['url']) ? $content['url'] : $url; | ||
65 | |||
66 | $this->stockEntry($entry, $content); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Stock entry with fetched or imported content. | ||
71 | * Will fall back to OpenGraph data if available. | ||
72 | * | ||
73 | * @param Entry $entry Entry to stock | ||
74 | * @param array $content Array with at least title, url & html | ||
75 | */ | ||
76 | private function stockEntry(Entry $entry, array $content) | ||
77 | { | ||
62 | $title = $content['title']; | 78 | $title = $content['title']; |
63 | if (!$title && isset($content['open_graph']['og_title'])) { | 79 | if (!$title && !empty($content['open_graph']['og_title'])) { |
64 | $title = $content['open_graph']['og_title']; | 80 | $title = $content['open_graph']['og_title']; |
65 | } | 81 | } |
66 | 82 | ||
@@ -68,18 +84,58 @@ class ContentProxy | |||
68 | if (false === $html) { | 84 | if (false === $html) { |
69 | $html = $this->fetchingErrorMessage; | 85 | $html = $this->fetchingErrorMessage; |
70 | 86 | ||
71 | if (isset($content['open_graph']['og_description'])) { | 87 | if (!empty($content['open_graph']['og_description'])) { |
72 | $html .= '<p><i>But we found a short description: </i></p>'; | 88 | $html .= '<p><i>But we found a short description: </i></p>'; |
73 | $html .= $content['open_graph']['og_description']; | 89 | $html .= $content['open_graph']['og_description']; |
74 | } | 90 | } |
75 | } | 91 | } |
76 | 92 | ||
77 | $entry->setUrl($content['url'] ?: $url); | 93 | $entry->setUrl($content['url']); |
78 | $entry->setTitle($title); | 94 | $entry->setTitle($title); |
79 | $entry->setContent($html); | 95 | $entry->setContent($html); |
80 | $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); | 96 | $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); |
81 | 97 | ||
82 | $entry->setLanguage(isset($content['language']) ? $content['language'] : ''); | 98 | if (!empty($content['date'])) { |
99 | $date = $content['date']; | ||
100 | |||
101 | // is it a timestamp? | ||
102 | if (filter_var($date, FILTER_VALIDATE_INT) !== false) { | ||
103 | $date = '@'.$content['date']; | ||
104 | } | ||
105 | |||
106 | try { | ||
107 | $entry->setPublishedAt(new \DateTime($date)); | ||
108 | } catch (\Exception $e) { | ||
109 | $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $content['url'], 'date' => $content['date']]); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if (!empty($content['authors']) && is_array($content['authors'])) { | ||
114 | $entry->setPublishedBy($content['authors']); | ||
115 | } | ||
116 | |||
117 | if (!empty($content['all_headers'])) { | ||
118 | $entry->setHeaders($content['all_headers']); | ||
119 | } | ||
120 | |||
121 | $this->validateAndSetLanguage( | ||
122 | $entry, | ||
123 | isset($content['language']) ? $content['language'] : null | ||
124 | ); | ||
125 | |||
126 | $this->validateAndSetPreviewPicture( | ||
127 | $entry, | ||
128 | isset($content['open_graph']['og_image']) ? $content['open_graph']['og_image'] : null | ||
129 | ); | ||
130 | |||
131 | // if content is an image, define it as a preview too | ||
132 | if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { | ||
133 | $this->validateAndSetPreviewPicture( | ||
134 | $entry, | ||
135 | $content['url'] | ||
136 | ); | ||
137 | } | ||
138 | |||
83 | $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : ''); | 139 | $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : ''); |
84 | $entry->setReadingTime(Utils::getReadingTime($html)); | 140 | $entry->setReadingTime(Utils::getReadingTime($html)); |
85 | 141 | ||
@@ -88,85 +144,73 @@ class ContentProxy | |||
88 | $entry->setDomainName($domainName); | 144 | $entry->setDomainName($domainName); |
89 | } | 145 | } |
90 | 146 | ||
91 | if (isset($content['open_graph']['og_image']) && $content['open_graph']['og_image']) { | ||
92 | $entry->setPreviewPicture($content['open_graph']['og_image']); | ||
93 | } | ||
94 | |||
95 | // if content is an image define as a preview too | ||
96 | if (isset($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { | ||
97 | $entry->setPreviewPicture($content['url']); | ||
98 | } | ||
99 | |||
100 | try { | 147 | try { |
101 | $this->tagger->tag($entry); | 148 | $this->tagger->tag($entry); |
102 | } catch (\Exception $e) { | 149 | } catch (\Exception $e) { |
103 | $this->logger->error('Error while trying to automatically tag an entry.', [ | 150 | $this->logger->error('Error while trying to automatically tag an entry.', [ |
104 | 'entry_url' => $url, | 151 | 'entry_url' => $content['url'], |
105 | 'error_msg' => $e->getMessage(), | 152 | 'error_msg' => $e->getMessage(), |
106 | ]); | 153 | ]); |
107 | } | 154 | } |
108 | |||
109 | return $entry; | ||
110 | } | 155 | } |
111 | 156 | ||
112 | /** | 157 | /** |
113 | * Assign some tags to an entry. | 158 | * Validate that the given content has at least a title, an html and a url. |
159 | * | ||
160 | * @param array $content | ||
114 | * | 161 | * |
115 | * @param Entry $entry | 162 | * @return bool true if valid otherwise false |
116 | * @param array|string $tags An array of tag or a string coma separated of tag | ||
117 | * @param array $entitiesReady Entities from the EntityManager which are persisted but not yet flushed | ||
118 | * It is mostly to fix duplicate tag on import @see http://stackoverflow.com/a/7879164/569101 | ||
119 | */ | 163 | */ |
120 | public function assignTagsToEntry(Entry $entry, $tags, array $entitiesReady = []) | 164 | private function validateContent(array $content) |
121 | { | 165 | { |
122 | if (!is_array($tags)) { | 166 | return !empty($content['title']) && !empty($content['html']) && !empty($content['url']); |
123 | $tags = explode(',', $tags); | 167 | } |
124 | } | ||
125 | |||
126 | // keeps only Tag entity from the "not yet flushed entities" | ||
127 | $tagsNotYetFlushed = []; | ||
128 | foreach ($entitiesReady as $entity) { | ||
129 | if ($entity instanceof Tag) { | ||
130 | $tagsNotYetFlushed[$entity->getLabel()] = $entity; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | foreach ($tags as $label) { | ||
135 | $label = trim($label); | ||
136 | 168 | ||
137 | // avoid empty tag | 169 | /** |
138 | if (0 === strlen($label)) { | 170 | * Use a Symfony validator to ensure the language is well formatted. |
139 | continue; | 171 | * |
140 | } | 172 | * @param Entry $entry |
173 | * @param string $value Language to validate | ||
174 | */ | ||
175 | private function validateAndSetLanguage($entry, $value) | ||
176 | { | ||
177 | // some lang are defined as fr-FR, es-ES. | ||
178 | // replacing - by _ might increase language support | ||
179 | $value = str_replace('-', '_', $value); | ||
141 | 180 | ||
142 | if (isset($tagsNotYetFlushed[$label])) { | 181 | $errors = $this->validator->validate( |
143 | $tagEntity = $tagsNotYetFlushed[$label]; | 182 | $value, |
144 | } else { | 183 | (new LocaleConstraint()) |
145 | $tagEntity = $this->tagRepository->findOneByLabel($label); | 184 | ); |
146 | 185 | ||
147 | if (is_null($tagEntity)) { | 186 | if (0 === count($errors)) { |
148 | $tagEntity = new Tag(); | 187 | $entry->setLanguage($value); |
149 | $tagEntity->setLabel($label); | ||
150 | } | ||
151 | } | ||
152 | 188 | ||
153 | // only add the tag on the entry if the relation doesn't exist | 189 | return; |
154 | if (false === $entry->getTags()->contains($tagEntity)) { | ||
155 | $entry->addTag($tagEntity); | ||
156 | } | ||
157 | } | 190 | } |
191 | |||
192 | $this->logger->warning('Language validation failed. '.(string) $errors); | ||
158 | } | 193 | } |
159 | 194 | ||
160 | /** | 195 | /** |
161 | * Validate that the given content as enough value to be used | 196 | * Use a Symfony validator to ensure the preview picture is a real url. |
162 | * instead of fetch the content from the url. | ||
163 | * | ||
164 | * @param array $content | ||
165 | * | 197 | * |
166 | * @return bool true if valid otherwise false | 198 | * @param Entry $entry |
199 | * @param string $value URL to validate | ||
167 | */ | 200 | */ |
168 | private function validateContent(array $content) | 201 | private function validateAndSetPreviewPicture($entry, $value) |
169 | { | 202 | { |
170 | return isset($content['title']) && isset($content['html']) && isset($content['url']) && isset($content['language']) && isset($content['content_type']); | 203 | $errors = $this->validator->validate( |
204 | $value, | ||
205 | (new UrlConstraint()) | ||
206 | ); | ||
207 | |||
208 | if (0 === count($errors)) { | ||
209 | $entry->setPreviewPicture($value); | ||
210 | |||
211 | return; | ||
212 | } | ||
213 | |||
214 | $this->logger->warning('PreviewPicture validation failed. '.(string) $errors); | ||
171 | } | 215 | } |
172 | } | 216 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/CryptoProxy.php b/src/Wallabag/CoreBundle/Helper/CryptoProxy.php new file mode 100644 index 00000000..e8b19cb9 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/CryptoProxy.php | |||
@@ -0,0 +1,86 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Helper; | ||
4 | |||
5 | use Psr\Log\LoggerInterface; | ||
6 | use Defuse\Crypto\Key; | ||
7 | use Defuse\Crypto\Crypto; | ||
8 | use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException; | ||
9 | |||
10 | /** | ||
11 | * This is a proxy to crypt and decrypt password used by SiteCredential entity. | ||
12 | * BTW, It might be re-use for sth else. | ||
13 | */ | ||
14 | class CryptoProxy | ||
15 | { | ||
16 | private $logger; | ||
17 | private $encryptionKey; | ||
18 | |||
19 | public function __construct($encryptionKeyPath, LoggerInterface $logger) | ||
20 | { | ||
21 | $this->logger = $logger; | ||
22 | |||
23 | if (!file_exists($encryptionKeyPath)) { | ||
24 | $key = Key::createNewRandomKey(); | ||
25 | |||
26 | file_put_contents($encryptionKeyPath, $key->saveToAsciiSafeString()); | ||
27 | chmod($encryptionKeyPath, 0600); | ||
28 | } | ||
29 | |||
30 | $this->encryptionKey = file_get_contents($encryptionKeyPath); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Ensure the given value will be crypted. | ||
35 | * | ||
36 | * @param string $secretValue Secret valye to crypt | ||
37 | * | ||
38 | * @return string | ||
39 | */ | ||
40 | public function crypt($secretValue) | ||
41 | { | ||
42 | $this->logger->debug('Crypto: crypting value: '.$this->mask($secretValue)); | ||
43 | |||
44 | return Crypto::encrypt($secretValue, $this->loadKey()); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * Ensure the given crypted value will be decrypted. | ||
49 | * | ||
50 | * @param string $cryptedValue The value to be decrypted | ||
51 | * | ||
52 | * @return string | ||
53 | */ | ||
54 | public function decrypt($cryptedValue) | ||
55 | { | ||
56 | $this->logger->debug('Crypto: decrypting value: '.$this->mask($cryptedValue)); | ||
57 | |||
58 | try { | ||
59 | return Crypto::decrypt($cryptedValue, $this->loadKey()); | ||
60 | } catch (WrongKeyOrModifiedCiphertextException $e) { | ||
61 | throw new \RuntimeException('Decrypt fail: '.$e->getMessage()); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Load the private key. | ||
67 | * | ||
68 | * @return Key | ||
69 | */ | ||
70 | private function loadKey() | ||
71 | { | ||
72 | return Key::loadFromAsciiSafeString($this->encryptionKey); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Keep first and last character and put some stars in between. | ||
77 | * | ||
78 | * @param string $value Value to mask | ||
79 | * | ||
80 | * @return string | ||
81 | */ | ||
82 | private function mask($value) | ||
83 | { | ||
84 | return strlen($value) > 0 ? $value[0].'*****'.$value[strlen($value) - 1] : 'Empty value'; | ||
85 | } | ||
86 | } | ||
diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php index 0d330d2a..ed888cdb 100644 --- a/src/Wallabag/CoreBundle/Helper/DownloadImages.php +++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php | |||
@@ -5,6 +5,7 @@ namespace Wallabag\CoreBundle\Helper; | |||
5 | use Psr\Log\LoggerInterface; | 5 | use Psr\Log\LoggerInterface; |
6 | use Symfony\Component\DomCrawler\Crawler; | 6 | use Symfony\Component\DomCrawler\Crawler; |
7 | use GuzzleHttp\Client; | 7 | use GuzzleHttp\Client; |
8 | use GuzzleHttp\Message\Response; | ||
8 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; | 9 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; |
9 | use Symfony\Component\Finder\Finder; | 10 | use Symfony\Component\Finder\Finder; |
10 | 11 | ||
@@ -54,7 +55,7 @@ class DownloadImages | |||
54 | $crawler = new Crawler($html); | 55 | $crawler = new Crawler($html); |
55 | $result = $crawler | 56 | $result = $crawler |
56 | ->filterXpath('//img') | 57 | ->filterXpath('//img') |
57 | ->extract(array('src')); | 58 | ->extract(['src']); |
58 | 59 | ||
59 | $relativePath = $this->getRelativePath($entryId); | 60 | $relativePath = $this->getRelativePath($entryId); |
60 | 61 | ||
@@ -66,6 +67,11 @@ class DownloadImages | |||
66 | continue; | 67 | continue; |
67 | } | 68 | } |
68 | 69 | ||
70 | // if image contains "&" and we can't find it in the html it might be because it's encoded as & | ||
71 | if (false !== stripos($image, '&') && false === stripos($html, $image)) { | ||
72 | $image = str_replace('&', '&', $image); | ||
73 | } | ||
74 | |||
69 | $html = str_replace($image, $imagePath, $html); | 75 | $html = str_replace($image, $imagePath, $html); |
70 | } | 76 | } |
71 | 77 | ||
@@ -111,13 +117,11 @@ class DownloadImages | |||
111 | return false; | 117 | return false; |
112 | } | 118 | } |
113 | 119 | ||
114 | $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); | 120 | $ext = $this->getExtensionFromResponse($res, $imagePath); |
115 | $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); | 121 | if (false === $res) { |
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; | 122 | return false; |
120 | } | 123 | } |
124 | |||
121 | $hashImage = hash('crc32', $absolutePath); | 125 | $hashImage = hash('crc32', $absolutePath); |
122 | $localPath = $folderPath.'/'.$hashImage.'.'.$ext; | 126 | $localPath = $folderPath.'/'.$hashImage.'.'.$ext; |
123 | 127 | ||
@@ -232,4 +236,45 @@ class DownloadImages | |||
232 | 236 | ||
233 | return false; | 237 | return false; |
234 | } | 238 | } |
239 | |||
240 | /** | ||
241 | * Retrieve and validate the extension from the response of the url of the image. | ||
242 | * | ||
243 | * @param Response $res Guzzle Response | ||
244 | * @param string $imagePath Path from the src image from the content (used for log only) | ||
245 | * | ||
246 | * @return string|false Extension name or false if validation failed | ||
247 | */ | ||
248 | private function getExtensionFromResponse(Response $res, $imagePath) | ||
249 | { | ||
250 | $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); | ||
251 | $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); | ||
252 | |||
253 | // ok header doesn't have the extension, try a different way | ||
254 | if (empty($ext)) { | ||
255 | $types = [ | ||
256 | 'jpeg' => "\xFF\xD8\xFF", | ||
257 | 'gif' => 'GIF', | ||
258 | 'png' => "\x89\x50\x4e\x47\x0d\x0a", | ||
259 | ]; | ||
260 | $bytes = substr((string) $res->getBody(), 0, 8); | ||
261 | |||
262 | foreach ($types as $type => $header) { | ||
263 | if (0 === strpos($bytes, $header)) { | ||
264 | $ext = $type; | ||
265 | break; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | $this->logger->debug('DownloadImages: Checking extension (alternative)', ['ext' => $ext]); | ||
270 | } | ||
271 | |||
272 | if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) { | ||
273 | $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping: '.$imagePath); | ||
274 | |||
275 | return false; | ||
276 | } | ||
277 | |||
278 | return $ext; | ||
279 | } | ||
235 | } | 280 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php index 1ac8feb1..43f5b119 100644 --- a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php +++ b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php | |||
@@ -13,8 +13,8 @@ use Psr\Log\LoggerInterface; | |||
13 | */ | 13 | */ |
14 | class HttpClientFactory | 14 | class HttpClientFactory |
15 | { | 15 | { |
16 | /** @var \GuzzleHttp\Event\SubscriberInterface */ | 16 | /** @var [\GuzzleHttp\Event\SubscriberInterface] */ |
17 | private $authenticatorSubscriber; | 17 | private $subscribers = []; |
18 | 18 | ||
19 | /** @var \GuzzleHttp\Cookie\CookieJar */ | 19 | /** @var \GuzzleHttp\Cookie\CookieJar */ |
20 | private $cookieJar; | 20 | private $cookieJar; |
@@ -25,14 +25,12 @@ class HttpClientFactory | |||
25 | /** | 25 | /** |
26 | * HttpClientFactory constructor. | 26 | * HttpClientFactory constructor. |
27 | * | 27 | * |
28 | * @param \GuzzleHttp\Event\SubscriberInterface $authenticatorSubscriber | 28 | * @param \GuzzleHttp\Cookie\CookieJar $cookieJar |
29 | * @param \GuzzleHttp\Cookie\CookieJar $cookieJar | 29 | * @param string $restrictedAccess This param is a kind of boolean. Values: 0 or 1 |
30 | * @param string $restrictedAccess this param is a kind of boolean. Values: 0 or 1 | 30 | * @param LoggerInterface $logger |
31 | * @param LoggerInterface $logger | ||
32 | */ | 31 | */ |
33 | public function __construct(SubscriberInterface $authenticatorSubscriber, CookieJar $cookieJar, $restrictedAccess, LoggerInterface $logger) | 32 | public function __construct(CookieJar $cookieJar, $restrictedAccess, LoggerInterface $logger) |
34 | { | 33 | { |
35 | $this->authenticatorSubscriber = $authenticatorSubscriber; | ||
36 | $this->cookieJar = $cookieJar; | 34 | $this->cookieJar = $cookieJar; |
37 | $this->restrictedAccess = $restrictedAccess; | 35 | $this->restrictedAccess = $restrictedAccess; |
38 | $this->logger = $logger; | 36 | $this->logger = $logger; |
@@ -53,8 +51,21 @@ class HttpClientFactory | |||
53 | $this->cookieJar->clear(); | 51 | $this->cookieJar->clear(); |
54 | // need to set the (shared) cookie jar | 52 | // need to set the (shared) cookie jar |
55 | $client = new Client(['handler' => new SafeCurlHandler(), 'defaults' => ['cookies' => $this->cookieJar]]); | 53 | $client = new Client(['handler' => new SafeCurlHandler(), 'defaults' => ['cookies' => $this->cookieJar]]); |
56 | $client->getEmitter()->attach($this->authenticatorSubscriber); | 54 | |
55 | foreach ($this->subscribers as $subscriber) { | ||
56 | $client->getEmitter()->attach($subscriber); | ||
57 | } | ||
57 | 58 | ||
58 | return $client; | 59 | return $client; |
59 | } | 60 | } |
61 | |||
62 | /** | ||
63 | * Adds a subscriber to the HTTP client. | ||
64 | * | ||
65 | * @param SubscriberInterface $subscriber | ||
66 | */ | ||
67 | public function addSubscriber(SubscriberInterface $subscriber) | ||
68 | { | ||
69 | $this->subscribers[] = $subscriber; | ||
70 | } | ||
60 | } | 71 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php index 7d3798b9..231a0b52 100644 --- a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php +++ b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php | |||
@@ -4,6 +4,7 @@ namespace Wallabag\CoreBundle\Helper; | |||
4 | 4 | ||
5 | use Pagerfanta\Adapter\AdapterInterface; | 5 | use Pagerfanta\Adapter\AdapterInterface; |
6 | use Pagerfanta\Pagerfanta; | 6 | use Pagerfanta\Pagerfanta; |
7 | use Wallabag\UserBundle\Entity\User; | ||
7 | use Symfony\Component\Routing\Router; | 8 | use Symfony\Component\Routing\Router; |
8 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | 9 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
9 | 10 | ||
@@ -20,16 +21,18 @@ class PreparePagerForEntries | |||
20 | 21 | ||
21 | /** | 22 | /** |
22 | * @param AdapterInterface $adapter | 23 | * @param AdapterInterface $adapter |
23 | * @param int $page | 24 | * @param User $user If user isn't logged in, we can force it (like for rss) |
24 | * | 25 | * |
25 | * @return null|Pagerfanta | 26 | * @return null|Pagerfanta |
26 | */ | 27 | */ |
27 | public function prepare(AdapterInterface $adapter, $page = 1) | 28 | public function prepare(AdapterInterface $adapter, User $user = null) |
28 | { | 29 | { |
29 | $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; | 30 | if (null === $user) { |
31 | $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; | ||
32 | } | ||
30 | 33 | ||
31 | if (null === $user || !is_object($user)) { | 34 | if (null === $user || !is_object($user)) { |
32 | return null; | 35 | return; |
33 | } | 36 | } |
34 | 37 | ||
35 | $entries = new Pagerfanta($adapter); | 38 | $entries = new Pagerfanta($adapter); |
diff --git a/src/Wallabag/CoreBundle/Helper/Redirect.php b/src/Wallabag/CoreBundle/Helper/Redirect.php index f78b7fe0..abc84d08 100644 --- a/src/Wallabag/CoreBundle/Helper/Redirect.php +++ b/src/Wallabag/CoreBundle/Helper/Redirect.php | |||
@@ -21,12 +21,13 @@ class Redirect | |||
21 | } | 21 | } |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * @param string $url URL to redirect | 24 | * @param string $url URL to redirect |
25 | * @param string $fallback Fallback URL if $url is null | 25 | * @param string $fallback Fallback URL if $url is null |
26 | * @param bool $ignoreActionMarkAsRead Ignore configured action when mark as read | ||
26 | * | 27 | * |
27 | * @return string | 28 | * @return string |
28 | */ | 29 | */ |
29 | public function to($url, $fallback = '') | 30 | public function to($url, $fallback = '', $ignoreActionMarkAsRead = false) |
30 | { | 31 | { |
31 | $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; | 32 | $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; |
32 | 33 | ||
@@ -34,7 +35,8 @@ class Redirect | |||
34 | return $url; | 35 | return $url; |
35 | } | 36 | } |
36 | 37 | ||
37 | if (Config::REDIRECT_TO_HOMEPAGE === $user->getConfig()->getActionMarkAsRead()) { | 38 | if (!$ignoreActionMarkAsRead && |
39 | Config::REDIRECT_TO_HOMEPAGE === $user->getConfig()->getActionMarkAsRead()) { | ||
38 | return $this->router->generate('homepage'); | 40 | return $this->router->generate('homepage'); |
39 | } | 41 | } |
40 | 42 | ||
diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php index b490e209..509d0dec 100644 --- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php | |||
@@ -8,18 +8,21 @@ use Wallabag\CoreBundle\Entity\Tag; | |||
8 | use Wallabag\CoreBundle\Repository\EntryRepository; | 8 | use Wallabag\CoreBundle\Repository\EntryRepository; |
9 | use Wallabag\CoreBundle\Repository\TagRepository; | 9 | use Wallabag\CoreBundle\Repository\TagRepository; |
10 | use Wallabag\UserBundle\Entity\User; | 10 | use Wallabag\UserBundle\Entity\User; |
11 | use Psr\Log\LoggerInterface; | ||
11 | 12 | ||
12 | class RuleBasedTagger | 13 | class RuleBasedTagger |
13 | { | 14 | { |
14 | private $rulerz; | 15 | private $rulerz; |
15 | private $tagRepository; | 16 | private $tagRepository; |
16 | private $entryRepository; | 17 | private $entryRepository; |
18 | private $logger; | ||
17 | 19 | ||
18 | public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository) | 20 | public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository, LoggerInterface $logger) |
19 | { | 21 | { |
20 | $this->rulerz = $rulerz; | 22 | $this->rulerz = $rulerz; |
21 | $this->tagRepository = $tagRepository; | 23 | $this->tagRepository = $tagRepository; |
22 | $this->entryRepository = $entryRepository; | 24 | $this->entryRepository = $entryRepository; |
25 | $this->logger = $logger; | ||
23 | } | 26 | } |
24 | 27 | ||
25 | /** | 28 | /** |
@@ -36,6 +39,11 @@ class RuleBasedTagger | |||
36 | continue; | 39 | continue; |
37 | } | 40 | } |
38 | 41 | ||
42 | $this->logger->info('Matching rule.', [ | ||
43 | 'rule' => $rule->getRule(), | ||
44 | 'tags' => $rule->getTags(), | ||
45 | ]); | ||
46 | |||
39 | foreach ($rule->getTags() as $label) { | 47 | foreach ($rule->getTags() as $label) { |
40 | $tag = $this->getTag($label); | 48 | $tag = $this->getTag($label); |
41 | 49 | ||
diff --git a/src/Wallabag/CoreBundle/Helper/TagsAssigner.php b/src/Wallabag/CoreBundle/Helper/TagsAssigner.php new file mode 100644 index 00000000..a2fb0b9a --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/TagsAssigner.php | |||
@@ -0,0 +1,75 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Helper; | ||
4 | |||
5 | use Wallabag\CoreBundle\Entity\Entry; | ||
6 | use Wallabag\CoreBundle\Entity\Tag; | ||
7 | use Wallabag\CoreBundle\Repository\TagRepository; | ||
8 | |||
9 | class TagsAssigner | ||
10 | { | ||
11 | /** | ||
12 | * @var TagRepository | ||
13 | */ | ||
14 | protected $tagRepository; | ||
15 | |||
16 | public function __construct(TagRepository $tagRepository) | ||
17 | { | ||
18 | $this->tagRepository = $tagRepository; | ||
19 | } | ||
20 | |||
21 | /** | ||
22 | * Assign some tags to an entry. | ||
23 | * | ||
24 | * @param Entry $entry | ||
25 | * @param array|string $tags An array of tag or a string coma separated of tag | ||
26 | * @param array $entitiesReady Entities from the EntityManager which are persisted but not yet flushed | ||
27 | * It is mostly to fix duplicate tag on import @see http://stackoverflow.com/a/7879164/569101 | ||
28 | * | ||
29 | * @return Tag[] | ||
30 | */ | ||
31 | public function assignTagsToEntry(Entry $entry, $tags, array $entitiesReady = []) | ||
32 | { | ||
33 | $tagsEntities = []; | ||
34 | |||
35 | if (!is_array($tags)) { | ||
36 | $tags = explode(',', $tags); | ||
37 | } | ||
38 | |||
39 | // keeps only Tag entity from the "not yet flushed entities" | ||
40 | $tagsNotYetFlushed = []; | ||
41 | foreach ($entitiesReady as $entity) { | ||
42 | if ($entity instanceof Tag) { | ||
43 | $tagsNotYetFlushed[$entity->getLabel()] = $entity; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | foreach ($tags as $label) { | ||
48 | $label = trim($label); | ||
49 | |||
50 | // avoid empty tag | ||
51 | if (0 === strlen($label)) { | ||
52 | continue; | ||
53 | } | ||
54 | |||
55 | if (isset($tagsNotYetFlushed[$label])) { | ||
56 | $tagEntity = $tagsNotYetFlushed[$label]; | ||
57 | } else { | ||
58 | $tagEntity = $this->tagRepository->findOneByLabel($label); | ||
59 | |||
60 | if (null === $tagEntity) { | ||
61 | $tagEntity = new Tag(); | ||
62 | $tagEntity->setLabel($label); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | // only add the tag on the entry if the relation doesn't exist | ||
67 | if (false === $entry->getTags()->contains($tagEntity)) { | ||
68 | $entry->addTag($tagEntity); | ||
69 | $tagsEntities[] = $tagEntity; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | return $tagsEntities; | ||
74 | } | ||
75 | } | ||
diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php new file mode 100644 index 00000000..b7f9da57 --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php | |||
@@ -0,0 +1,25 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Operator\Doctrine; | ||
4 | |||
5 | /** | ||
6 | * Provides a "notmatches" operator used for tagging rules. | ||
7 | * | ||
8 | * It asserts that a given pattern is not contained in a subject, in a | ||
9 | * case-insensitive way. | ||
10 | * | ||
11 | * This operator will be used to compile tagging rules in DQL, usable | ||
12 | * by Doctrine ORM. | ||
13 | * It's registered in RulerZ using a service (wallabag.operator.doctrine.notmatches); | ||
14 | */ | ||
15 | class NotMatches | ||
16 | { | ||
17 | public function __invoke($subject, $pattern) | ||
18 | { | ||
19 | if ($pattern[0] === "'") { | ||
20 | $pattern = sprintf("'%%%s%%'", substr($pattern, 1, -1)); | ||
21 | } | ||
22 | |||
23 | return sprintf('UPPER(%s) NOT LIKE UPPER(%s)', $subject, $pattern); | ||
24 | } | ||
25 | } | ||
diff --git a/src/Wallabag/CoreBundle/Operator/PHP/NotMatches.php b/src/Wallabag/CoreBundle/Operator/PHP/NotMatches.php new file mode 100644 index 00000000..68b2676f --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/PHP/NotMatches.php | |||
@@ -0,0 +1,21 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Operator\PHP; | ||
4 | |||
5 | /** | ||
6 | * Provides a "notmatches" operator used for tagging rules. | ||
7 | * | ||
8 | * It asserts that a given pattern is not contained in a subject, in a | ||
9 | * case-insensitive way. | ||
10 | * | ||
11 | * This operator will be used to compile tagging rules in PHP, usable | ||
12 | * directly on Entry objects for instance. | ||
13 | * It's registered in RulerZ using a service (wallabag.operator.array.notmatches); | ||
14 | */ | ||
15 | class NotMatches | ||
16 | { | ||
17 | public function __invoke($subject, $pattern) | ||
18 | { | ||
19 | return stripos($subject, $pattern) === false; | ||
20 | } | ||
21 | } | ||
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index 4071301d..9bda4e15 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php | |||
@@ -135,6 +135,7 @@ class EntryRepository extends EntityRepository | |||
135 | * @param int $userId | 135 | * @param int $userId |
136 | * @param bool $isArchived | 136 | * @param bool $isArchived |
137 | * @param bool $isStarred | 137 | * @param bool $isStarred |
138 | * @param bool $isPublic | ||
138 | * @param string $sort | 139 | * @param string $sort |
139 | * @param string $order | 140 | * @param string $order |
140 | * @param int $since | 141 | * @param int $since |
@@ -142,18 +143,22 @@ class EntryRepository extends EntityRepository | |||
142 | * | 143 | * |
143 | * @return array | 144 | * @return array |
144 | */ | 145 | */ |
145 | public function findEntries($userId, $isArchived = null, $isStarred = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') | 146 | public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') |
146 | { | 147 | { |
147 | $qb = $this->createQueryBuilder('e') | 148 | $qb = $this->createQueryBuilder('e') |
148 | ->leftJoin('e.tags', 't') | 149 | ->leftJoin('e.tags', 't') |
149 | ->where('e.user =:userId')->setParameter('userId', $userId); | 150 | ->where('e.user =:userId')->setParameter('userId', $userId); |
150 | 151 | ||
151 | if (null !== $isArchived) { | 152 | if (null !== $isArchived) { |
152 | $qb->andWhere('e.isArchived =:isArchived')->setParameter('isArchived', (bool) $isArchived); | 153 | $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived); |
153 | } | 154 | } |
154 | 155 | ||
155 | if (null !== $isStarred) { | 156 | if (null !== $isStarred) { |
156 | $qb->andWhere('e.isStarred =:isStarred')->setParameter('isStarred', (bool) $isStarred); | 157 | $qb->andWhere('e.isStarred = :isStarred')->setParameter('isStarred', (bool) $isStarred); |
158 | } | ||
159 | |||
160 | if (null !== $isPublic) { | ||
161 | $qb->andWhere('e.uid IS '.(true === $isPublic ? 'NOT' : '').' NULL'); | ||
157 | } | 162 | } |
158 | 163 | ||
159 | if ($since > 0) { | 164 | if ($since > 0) { |
@@ -328,7 +333,7 @@ class EntryRepository extends EntityRepository | |||
328 | * | 333 | * |
329 | * @return int | 334 | * @return int |
330 | */ | 335 | */ |
331 | public function countAllEntriesByUsername($userId) | 336 | public function countAllEntriesByUser($userId) |
332 | { | 337 | { |
333 | $qb = $this->createQueryBuilder('e') | 338 | $qb = $this->createQueryBuilder('e') |
334 | ->select('count(e)') | 339 | ->select('count(e)') |
@@ -371,4 +376,42 @@ class EntryRepository extends EntityRepository | |||
371 | ->setParameter('userId', $userId) | 376 | ->setParameter('userId', $userId) |
372 | ->execute(); | 377 | ->execute(); |
373 | } | 378 | } |
379 | |||
380 | public function removeArchivedByUserId($userId) | ||
381 | { | ||
382 | $this->getEntityManager() | ||
383 | ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId AND e.isArchived = TRUE') | ||
384 | ->setParameter('userId', $userId) | ||
385 | ->execute(); | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * Get id and url from all entries | ||
390 | * Used for the clean-duplicates command. | ||
391 | */ | ||
392 | public function getAllEntriesIdAndUrl($userId) | ||
393 | { | ||
394 | $qb = $this->createQueryBuilder('e') | ||
395 | ->select('e.id, e.url') | ||
396 | ->where('e.user = :userid')->setParameter(':userid', $userId); | ||
397 | |||
398 | return $qb->getQuery()->getArrayResult(); | ||
399 | } | ||
400 | |||
401 | /** | ||
402 | * Find all entries by url and owner. | ||
403 | * | ||
404 | * @param $url | ||
405 | * @param $userId | ||
406 | * | ||
407 | * @return array | ||
408 | */ | ||
409 | public function findAllByUrlAndUserId($url, $userId) | ||
410 | { | ||
411 | return $this->createQueryBuilder('e') | ||
412 | ->where('e.url = :url')->setParameter('url', urldecode($url)) | ||
413 | ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) | ||
414 | ->getQuery() | ||
415 | ->getResult(); | ||
416 | } | ||
374 | } | 417 | } |
diff --git a/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php new file mode 100644 index 00000000..36906761 --- /dev/null +++ b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php | |||
@@ -0,0 +1,47 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Repository; | ||
4 | |||
5 | use Wallabag\CoreBundle\Helper\CryptoProxy; | ||
6 | |||
7 | /** | ||
8 | * SiteCredentialRepository. | ||
9 | */ | ||
10 | class SiteCredentialRepository extends \Doctrine\ORM\EntityRepository | ||
11 | { | ||
12 | private $cryptoProxy; | ||
13 | |||
14 | public function setCrypto(CryptoProxy $cryptoProxy) | ||
15 | { | ||
16 | $this->cryptoProxy = $cryptoProxy; | ||
17 | } | ||
18 | |||
19 | /** | ||
20 | * Retrieve one username/password for the given host and userId. | ||
21 | * | ||
22 | * @param string $host | ||
23 | * @param int $userId | ||
24 | * | ||
25 | * @return null|array | ||
26 | */ | ||
27 | public function findOneByHostAndUser($host, $userId) | ||
28 | { | ||
29 | $res = $this->createQueryBuilder('s') | ||
30 | ->select('s.username', 's.password') | ||
31 | ->where('s.host = :hostname')->setParameter('hostname', $host) | ||
32 | ->andWhere('s.user = :userId')->setParameter('userId', $userId) | ||
33 | ->setMaxResults(1) | ||
34 | ->getQuery() | ||
35 | ->getOneOrNullResult(); | ||
36 | |||
37 | if (null === $res) { | ||
38 | return; | ||
39 | } | ||
40 | |||
41 | // decrypt user & password before returning them | ||
42 | $res['username'] = $this->cryptoProxy->decrypt($res['username']); | ||
43 | $res['password'] = $this->cryptoProxy->decrypt($res['password']); | ||
44 | |||
45 | return $res; | ||
46 | } | ||
47 | } | ||
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php index 2182df25..6c63a6a2 100644 --- a/src/Wallabag/CoreBundle/Repository/TagRepository.php +++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php | |||
@@ -76,4 +76,24 @@ class TagRepository extends EntityRepository | |||
76 | ->getQuery() | 76 | ->getQuery() |
77 | ->getSingleResult(); | 77 | ->getSingleResult(); |
78 | } | 78 | } |
79 | |||
80 | public function findForArchivedArticlesByUser($userId) | ||
81 | { | ||
82 | $ids = $this->createQueryBuilder('t') | ||
83 | ->select('t.id') | ||
84 | ->leftJoin('t.entries', 'e') | ||
85 | ->where('e.user = :userId')->setParameter('userId', $userId) | ||
86 | ->andWhere('e.isArchived = true') | ||
87 | ->groupBy('t.id') | ||
88 | ->orderBy('t.slug') | ||
89 | ->getQuery() | ||
90 | ->getArrayResult(); | ||
91 | |||
92 | $tags = []; | ||
93 | foreach ($ids as $id) { | ||
94 | $tags[] = $this->find($id); | ||
95 | } | ||
96 | |||
97 | return $tags; | ||
98 | } | ||
79 | } | 99 | } |
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 51d6ab47..e09b0f18 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml | |||
@@ -41,6 +41,7 @@ services: | |||
41 | arguments: | 41 | arguments: |
42 | - | 42 | - |
43 | error_message: '%wallabag_core.fetching_error_message%' | 43 | error_message: '%wallabag_core.fetching_error_message%' |
44 | error_message_title: '%wallabag_core.fetching_error_message_title%' | ||
44 | - "@wallabag_core.guzzle.http_client" | 45 | - "@wallabag_core.guzzle.http_client" |
45 | - "@wallabag_core.graby.config_builder" | 46 | - "@wallabag_core.graby.config_builder" |
46 | calls: | 47 | calls: |
@@ -62,7 +63,11 @@ services: | |||
62 | class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder | 63 | class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder |
63 | arguments: | 64 | arguments: |
64 | - "@wallabag_core.graby.config_builder" | 65 | - "@wallabag_core.graby.config_builder" |
65 | - "%sites_credentials%" | 66 | - "@security.token_storage" |
67 | - "@wallabag_core.site_credential_repository" | ||
68 | - '@logger' | ||
69 | tags: | ||
70 | - { name: monolog.logger, channel: graby } | ||
66 | 71 | ||
67 | # service alias override | 72 | # service alias override |
68 | bd_guzzle_site_authenticator.site_config_builder: | 73 | bd_guzzle_site_authenticator.site_config_builder: |
@@ -71,10 +76,11 @@ services: | |||
71 | wallabag_core.guzzle.http_client_factory: | 76 | wallabag_core.guzzle.http_client_factory: |
72 | class: Wallabag\CoreBundle\Helper\HttpClientFactory | 77 | class: Wallabag\CoreBundle\Helper\HttpClientFactory |
73 | arguments: | 78 | arguments: |
74 | - "@bd_guzzle_site_authenticator.authenticator_subscriber" | ||
75 | - "@wallabag_core.guzzle.cookie_jar" | 79 | - "@wallabag_core.guzzle.cookie_jar" |
76 | - '@=service(''craue_config'').get(''restricted_access'')' | 80 | - '@=service(''craue_config'').get(''restricted_access'')' |
77 | - '@logger' | 81 | - '@logger' |
82 | calls: | ||
83 | - ["addSubscriber", ["@bd_guzzle_site_authenticator.authenticator_subscriber"]] | ||
78 | 84 | ||
79 | wallabag_core.guzzle.cookie_jar: | 85 | wallabag_core.guzzle.cookie_jar: |
80 | class: GuzzleHttp\Cookie\FileCookieJar | 86 | class: GuzzleHttp\Cookie\FileCookieJar |
@@ -85,16 +91,22 @@ services: | |||
85 | arguments: | 91 | arguments: |
86 | - "@wallabag_core.graby" | 92 | - "@wallabag_core.graby" |
87 | - "@wallabag_core.rule_based_tagger" | 93 | - "@wallabag_core.rule_based_tagger" |
88 | - "@wallabag_core.tag_repository" | 94 | - "@validator" |
89 | - "@logger" | 95 | - "@logger" |
90 | - '%wallabag_core.fetching_error_message%' | 96 | - '%wallabag_core.fetching_error_message%' |
91 | 97 | ||
98 | wallabag_core.tags_assigner: | ||
99 | class: Wallabag\CoreBundle\Helper\TagsAssigner | ||
100 | arguments: | ||
101 | - "@wallabag_core.tag_repository" | ||
102 | |||
92 | wallabag_core.rule_based_tagger: | 103 | wallabag_core.rule_based_tagger: |
93 | class: Wallabag\CoreBundle\Helper\RuleBasedTagger | 104 | class: Wallabag\CoreBundle\Helper\RuleBasedTagger |
94 | arguments: | 105 | arguments: |
95 | - "@rulerz" | 106 | - "@rulerz" |
96 | - "@wallabag_core.tag_repository" | 107 | - "@wallabag_core.tag_repository" |
97 | - "@wallabag_core.entry_repository" | 108 | - "@wallabag_core.entry_repository" |
109 | - "@logger" | ||
98 | 110 | ||
99 | # repository as a service | 111 | # repository as a service |
100 | wallabag_core.entry_repository: | 112 | wallabag_core.entry_repository: |
@@ -109,10 +121,18 @@ services: | |||
109 | arguments: | 121 | arguments: |
110 | - WallabagCoreBundle:Tag | 122 | - WallabagCoreBundle:Tag |
111 | 123 | ||
124 | wallabag_core.site_credential_repository: | ||
125 | class: Wallabag\CoreBundle\Repository\SiteCredentialRepository | ||
126 | factory: [ "@doctrine.orm.default_entity_manager", getRepository ] | ||
127 | arguments: | ||
128 | - WallabagCoreBundle:SiteCredential | ||
129 | calls: | ||
130 | - [ setCrypto, [ "@wallabag_core.helper.crypto_proxy" ] ] | ||
131 | |||
112 | wallabag_core.helper.entries_export: | 132 | wallabag_core.helper.entries_export: |
113 | class: Wallabag\CoreBundle\Helper\EntriesExport | 133 | class: Wallabag\CoreBundle\Helper\EntriesExport |
114 | arguments: | 134 | arguments: |
115 | - '@=service(''craue_config'').get(''wallabag_url'')' | 135 | - '%domain_name%' |
116 | - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png | 136 | - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png |
117 | 137 | ||
118 | wallabag.operator.array.matches: | 138 | wallabag.operator.array.matches: |
@@ -125,6 +145,16 @@ services: | |||
125 | tags: | 145 | tags: |
126 | - { name: rulerz.operator, target: doctrine, operator: matches, inline: true } | 146 | - { name: rulerz.operator, target: doctrine, operator: matches, inline: true } |
127 | 147 | ||
148 | wallabag.operator.array.notmatches: | ||
149 | class: Wallabag\CoreBundle\Operator\PHP\NotMatches | ||
150 | tags: | ||
151 | - { name: rulerz.operator, target: native, operator: notmatches } | ||
152 | |||
153 | wallabag.operator.doctrine.notmatches: | ||
154 | class: Wallabag\CoreBundle\Operator\Doctrine\NotMatches | ||
155 | tags: | ||
156 | - { name: rulerz.operator, target: doctrine, operator: notmatches, inline: true } | ||
157 | |||
128 | wallabag_core.helper.redirect: | 158 | wallabag_core.helper.redirect: |
129 | class: Wallabag\CoreBundle\Helper\Redirect | 159 | class: Wallabag\CoreBundle\Helper\Redirect |
130 | arguments: | 160 | arguments: |
@@ -175,8 +205,14 @@ services: | |||
175 | arguments: | 205 | arguments: |
176 | - "@wallabag_core.entry.download_images.client" | 206 | - "@wallabag_core.entry.download_images.client" |
177 | - "%kernel.root_dir%/../web/assets/images" | 207 | - "%kernel.root_dir%/../web/assets/images" |
178 | - '@=service(''craue_config'').get(''wallabag_url'')' | 208 | - '%domain_name%' |
179 | - "@logger" | 209 | - "@logger" |
180 | 210 | ||
181 | wallabag_core.entry.download_images.client: | 211 | wallabag_core.entry.download_images.client: |
182 | class: GuzzleHttp\Client | 212 | class: GuzzleHttp\Client |
213 | |||
214 | wallabag_core.helper.crypto_proxy: | ||
215 | class: Wallabag\CoreBundle\Helper\CryptoProxy | ||
216 | arguments: | ||
217 | - "%wallabag_core.site_credentials.encryption_key_path%" | ||
218 | - "@logger" | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index a52b579a..5229ac73 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | # save_link: 'Save a link' | 32 | # save_link: 'Save a link' |
33 | back_to_unread: 'Tilbage til de ulæste artikler' | 33 | back_to_unread: 'Tilbage til de ulæste artikler' |
34 | # users_management: 'Users management' | 34 | # users_management: 'Users management' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Tilføj ny artikel' | 37 | add_new_entry: 'Tilføj ny artikel' |
37 | search: 'Søg' | 38 | search: 'Søg' |
@@ -76,6 +77,7 @@ config: | |||
76 | # redirect_current_page: 'To the current page' | 77 | # redirect_current_page: 'To the current page' |
77 | pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer | 78 | pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer |
78 | # android_configuration: Configure your Android application | 79 | # android_configuration: Configure your Android application |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." | 81 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." |
80 | # help_items_per_page: "You can change the number of articles displayed on each page." | 82 | # help_items_per_page: "You can change the number of articles displayed on each page." |
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." | 83 | # 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." |
@@ -89,9 +91,10 @@ config: | |||
89 | token_reset: 'Nulstil token' | 91 | token_reset: 'Nulstil token' |
90 | rss_links: 'RSS-Links' | 92 | rss_links: 'RSS-Links' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'ulæst' | 94 | unread: 'Ulæst' |
93 | starred: 'favoritter' | 95 | starred: 'Favoritter' |
94 | archive: 'arkiv' | 96 | archive: 'Arkiv' |
97 | # all: 'All' | ||
95 | # rss_limit: 'Number of items in the feed' | 98 | # rss_limit: 'Number of items in the feed' |
96 | form_user: | 99 | form_user: |
97 | # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" | 100 | # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" |
@@ -110,6 +113,7 @@ config: | |||
110 | # annotations: Remove ALL annotations | 113 | # annotations: Remove ALL annotations |
111 | # tags: Remove ALL tags | 114 | # tags: Remove ALL tags |
112 | # entries: Remove ALL entries | 115 | # entries: Remove ALL entries |
116 | # archived: Remove ALL archived entries | ||
113 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | 117 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) |
114 | form_password: | 118 | form_password: |
115 | # description: "You can change your password here. Your new password should by at least 8 characters long." | 119 | # description: "You can change your password here. Your new password should by at least 8 characters long." |
@@ -154,6 +158,7 @@ config: | |||
154 | # or: 'One rule OR another' | 158 | # or: 'One rule OR another' |
155 | # and: 'One rule AND another' | 159 | # and: 'One rule AND another' |
156 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' | 160 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Ulæst' | 190 | unread_label: 'Ulæst' |
186 | preview_picture_label: 'Har et vist billede' | 191 | preview_picture_label: 'Har et vist billede' |
187 | preview_picture_help: 'Forhåndsvis billede' | 192 | preview_picture_help: 'Forhåndsvis billede' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'Sprog' | 195 | language_label: 'Sprog' |
189 | # http_status_label: 'HTTP status' | 196 | # http_status_label: 'HTTP status' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 231 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Oprettelsesdato' | 232 | created_at: 'Oprettelsesdato' |
233 | # published_at: 'Publication date' | ||
234 | # published_by: 'Published by' | ||
226 | new: | 235 | new: |
227 | page_title: 'Gem ny artikel' | 236 | page_title: 'Gem ny artikel' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | # page_title: 'Edit an entry' | 243 | # page_title: 'Edit an entry' |
235 | # title_label: 'Title' | 244 | # title_label: 'Title' |
236 | url_label: 'Url' | 245 | url_label: 'Url' |
237 | # is_public_label: 'Public' | ||
238 | save_label: 'Gem' | 246 | save_label: 'Gem' |
239 | public: | 247 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href=%wallabag_instance%'>wallabag</a>" | 248 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | # delete: "Are you sure you want to remove that article?" | ||
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'Om' | 254 | page_title: 'Om' |
@@ -510,6 +521,28 @@ user: | |||
510 | # delete: Delete | 521 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 522 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 523 | # back_to_list: Back to list |
524 | search: | ||
525 | # placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | # list: | ||
533 | # actions: Actions | ||
534 | # edit_action: Edit | ||
535 | # yes: Yes | ||
536 | # no: No | ||
537 | # create_new_one: Create a new credential | ||
538 | # form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | # save: Save | ||
543 | # delete: Delete | ||
544 | # delete_confirm: Are you sure? | ||
545 | # back_to_list: Back to list | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | # page_title: An error occurred | 548 | # page_title: An error occurred |
@@ -528,6 +561,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 561 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 562 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 563 | # entries_reset: Entries reset |
564 | # archived_reset: Archived entries deleted | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | # entry_already_saved: 'Entry already saved on %date%' | 567 | # entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 596 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 597 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 598 | # deleted: 'User "%username%" deleted' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml index 35cb4b5b..996f173a 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: 'Link speichern' | 32 | save_link: 'Link speichern' |
33 | back_to_unread: 'Zurück zu ungelesenen Artikeln' | 33 | back_to_unread: 'Zurück zu ungelesenen Artikeln' |
34 | users_management: 'Benutzerverwaltung' | 34 | users_management: 'Benutzerverwaltung' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Neuen Artikel hinzufügen' | 37 | add_new_entry: 'Neuen Artikel hinzufügen' |
37 | search: 'Suche' | 38 | search: 'Suche' |
@@ -76,6 +77,7 @@ config: | |||
76 | redirect_current_page: 'Zur aktuellen Seite' | 77 | redirect_current_page: 'Zur aktuellen Seite' |
77 | pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren | 78 | pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren |
78 | android_configuration: Konfiguriere deine Android Application | 79 | android_configuration: Konfiguriere deine Android Application |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | help_theme: "wallabag ist anpassbar. Du kannst dein bevorzugtes Theme hier auswählen." | 81 | help_theme: "wallabag ist anpassbar. Du kannst dein bevorzugtes Theme hier auswählen." |
80 | help_items_per_page: "Du kannst die Nummer von Artikeln pro Seite anpassen." | 82 | help_items_per_page: "Du kannst die Nummer von Artikeln pro Seite anpassen." |
81 | help_reading_speed: "wallabag berechnet eine Lesezeit pro Artikel. Hier kannst du definieren, ob du ein schneller oder langsamer Leser bist. wallabag wird die Lesezeiten danach neu berechnen." | 83 | help_reading_speed: "wallabag berechnet eine Lesezeit pro Artikel. Hier kannst du definieren, ob du ein schneller oder langsamer Leser bist. wallabag wird die Lesezeiten danach neu berechnen." |
@@ -92,6 +94,7 @@ config: | |||
92 | unread: 'Ungelesene' | 94 | unread: 'Ungelesene' |
93 | starred: 'Favoriten' | 95 | starred: 'Favoriten' |
94 | archive: 'Archivierte' | 96 | archive: 'Archivierte' |
97 | # all: 'All' | ||
95 | rss_limit: 'Anzahl der Einträge pro Feed' | 98 | rss_limit: 'Anzahl der Einträge pro Feed' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "Wenn du die Zwei-Faktor-Authentifizierung aktivierst, erhältst du eine E-Mail mit einem Code bei jeder nicht vertrauenswürdigen Verbindung" | 100 | two_factor_description: "Wenn du die Zwei-Faktor-Authentifizierung aktivierst, erhältst du eine E-Mail mit einem Code bei jeder nicht vertrauenswürdigen Verbindung" |
@@ -110,6 +113,7 @@ config: | |||
110 | annotations: Entferne ALLE Annotationen | 113 | annotations: Entferne ALLE Annotationen |
111 | tags: Entferne ALLE Tags | 114 | tags: Entferne ALLE Tags |
112 | entries: Entferne ALLE Einträge | 115 | entries: Entferne ALLE Einträge |
116 | archived: Entferne ALLE archivierten Einträge | ||
113 | confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN) | 117 | confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN) |
114 | form_password: | 118 | form_password: |
115 | description: "Hier kannst du dein Kennwort ändern. Dieses sollte mindestens acht Zeichen enthalten." | 119 | description: "Hier kannst du dein Kennwort ändern. Dieses sollte mindestens acht Zeichen enthalten." |
@@ -154,6 +158,7 @@ config: | |||
154 | or: 'Eine Regel ODER die andere' | 158 | or: 'Eine Regel ODER die andere' |
155 | and: 'Eine Regel UND eine andere' | 159 | and: 'Eine Regel UND eine andere' |
156 | matches: 'Testet, ob eine <i>Variable</i> auf eine <i>Suche</i> zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).<br />Beispiel: <code>title matches "Fußball"</code>' | 160 | matches: 'Testet, ob eine <i>Variable</i> auf eine <i>Suche</i> zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).<br />Beispiel: <code>title matches "Fußball"</code>' |
161 | notmatches: 'Testet, ob ein <i>Titel</i> nicht auf eine <i>Suche</i> zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).<br />Beispiel: <code>title notmatches "Fußball"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Ungelesene' | 190 | unread_label: 'Ungelesene' |
186 | preview_picture_label: 'Vorschaubild vorhanden' | 191 | preview_picture_label: 'Vorschaubild vorhanden' |
187 | preview_picture_help: 'Vorschaubild' | 192 | preview_picture_help: 'Vorschaubild' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'Sprache' | 195 | language_label: 'Sprache' |
189 | http_status_label: 'HTTP-Status' | 196 | http_status_label: 'HTTP-Status' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} Keine Anmerkungen|{1} Eine Anmerkung|]1,Inf[ %count% Anmerkungen' | 231 | annotations_on_the_entry: '{0} Keine Anmerkungen|{1} Eine Anmerkung|]1,Inf[ %count% Anmerkungen' |
225 | created_at: 'Erstellungsdatum' | 232 | created_at: 'Erstellungsdatum' |
233 | published_at: 'Erscheinungsdatum' | ||
234 | published_by: 'Veröffentlicht von' | ||
226 | new: | 235 | new: |
227 | page_title: 'Neuen Artikel speichern' | 236 | page_title: 'Neuen Artikel speichern' |
228 | placeholder: 'https://website.de' | 237 | placeholder: 'https://website.de' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'Eintrag bearbeiten' | 243 | page_title: 'Eintrag bearbeiten' |
235 | title_label: 'Titel' | 244 | title_label: 'Titel' |
236 | url_label: 'URL' | 245 | url_label: 'URL' |
237 | is_public_label: 'Öffentlich' | ||
238 | save_label: 'Speichern' | 246 | save_label: 'Speichern' |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "Dieser Artikel wurde mittels <a href='%wallabag_instance%'>wallabag</a> geteilt" | 248 | shared_by_wallabag: "Dieser Artikel wurde von %username% mittels <a href='%wallabag_instance%'>wallabag</a> geteilt" |
249 | confirm: | ||
250 | delete: "Bist du sicher, dass du diesen Artikel löschen möchtest?" | ||
251 | delete_tag: "Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'Über' | 254 | page_title: 'Über' |
@@ -510,6 +521,28 @@ user: | |||
510 | delete: Löschen | 521 | delete: Löschen |
511 | delete_confirm: Bist du sicher? | 522 | delete_confirm: Bist du sicher? |
512 | back_to_list: Zurück zur Liste | 523 | back_to_list: Zurück zur Liste |
524 | search: | ||
525 | placeholder: Filtere nach Benutzer oder E-Mail-Adresse | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | list: | ||
533 | actions: Aktionen | ||
534 | edit_action: Bearbeiten | ||
535 | yes: Ja | ||
536 | no: Nein | ||
537 | # create_new_one: Create a new credential | ||
538 | form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | save: Speichern | ||
543 | delete: Löschen | ||
544 | delete_confirm: Bist du sicher? | ||
545 | back_to_list: Zurück zur Liste | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | page_title: Ein Fehler ist aufgetreten | 548 | page_title: Ein Fehler ist aufgetreten |
@@ -528,6 +561,7 @@ flashes: | |||
528 | annotations_reset: Anmerkungen zurücksetzen | 561 | annotations_reset: Anmerkungen zurücksetzen |
529 | tags_reset: Tags zurücksetzen | 562 | tags_reset: Tags zurücksetzen |
530 | entries_reset: Einträge zurücksetzen | 563 | entries_reset: Einträge zurücksetzen |
564 | archived_reset: Archiverte Einträge zurücksetzen | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'Eintrag bereits am %date% gespeichert' | 567 | entry_already_saved: 'Eintrag bereits am %date% gespeichert' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'Benutzer "%username%" hinzugefügt' | 596 | added: 'Benutzer "%username%" hinzugefügt' |
563 | updated: 'Benutzer "%username%" aktualisiert' | 597 | updated: 'Benutzer "%username%" aktualisiert' |
564 | deleted: 'Benutzer "%username%" gelöscht' | 598 | deleted: 'Benutzer "%username%" gelöscht' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index c72e5958..aa1cd1a9 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: 'Save a link' | 32 | save_link: 'Save a link' |
33 | back_to_unread: 'Back to unread articles' | 33 | back_to_unread: 'Back to unread articles' |
34 | users_management: 'Users management' | 34 | users_management: 'Users management' |
35 | site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Add a new entry' | 37 | add_new_entry: 'Add a new entry' |
37 | search: 'Search' | 38 | search: 'Search' |
@@ -76,6 +77,7 @@ config: | |||
76 | redirect_current_page: 'To the current page' | 77 | redirect_current_page: 'To the current page' |
77 | pocket_consumer_key_label: Consumer key for Pocket to import contents | 78 | pocket_consumer_key_label: Consumer key for Pocket to import contents |
78 | android_configuration: Configure your Android application | 79 | android_configuration: Configure your Android application |
80 | android_instruction: "Touch here to prefill your Android application" | ||
79 | help_theme: "wallabag is customizable. You can choose your prefered theme here." | 81 | help_theme: "wallabag is customizable. You can choose your prefered theme here." |
80 | help_items_per_page: "You can change the number of articles displayed on each page." | 82 | help_items_per_page: "You can change the number of articles displayed on each page." |
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." | 83 | 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." |
@@ -89,9 +91,10 @@ config: | |||
89 | token_reset: 'Regenerate your token' | 91 | token_reset: 'Regenerate your token' |
90 | rss_links: 'RSS links' | 92 | rss_links: 'RSS links' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'unread' | 94 | unread: 'Unread' |
93 | starred: 'starred' | 95 | starred: 'Starred' |
94 | archive: 'archived' | 96 | archive: 'Archived' |
97 | all: 'All' | ||
95 | rss_limit: 'Number of items in the feed' | 98 | rss_limit: 'Number of items in the feed' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connection." | 100 | two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connection." |
@@ -110,6 +113,7 @@ config: | |||
110 | annotations: Remove ALL annotations | 113 | annotations: Remove ALL annotations |
111 | tags: Remove ALL tags | 114 | tags: Remove ALL tags |
112 | entries: Remove ALL entries | 115 | entries: Remove ALL entries |
116 | archived: Remove ALL archived entries | ||
113 | confirm: Are you really sure? (THIS CAN'T BE UNDONE) | 117 | confirm: Are you really sure? (THIS CAN'T BE UNDONE) |
114 | form_password: | 118 | form_password: |
115 | description: "You can change your password here. Your new password should by at least 8 characters long." | 119 | description: "You can change your password here. Your new password should by at least 8 characters long." |
@@ -154,6 +158,7 @@ config: | |||
154 | or: 'One rule OR another' | 158 | or: 'One rule OR another' |
155 | and: 'One rule AND another' | 159 | and: 'One rule AND another' |
156 | matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' | 160 | matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' |
161 | notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Unread' | 190 | unread_label: 'Unread' |
186 | preview_picture_label: 'Has a preview picture' | 191 | preview_picture_label: 'Has a preview picture' |
187 | preview_picture_help: 'Preview picture' | 192 | preview_picture_help: 'Preview picture' |
193 | is_public_label: 'Has a public link' | ||
194 | is_public_help: 'Public link' | ||
188 | language_label: 'Language' | 195 | language_label: 'Language' |
189 | http_status_label: 'HTTP status' | 196 | http_status_label: 'HTTP status' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 231 | annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Creation date' | 232 | created_at: 'Creation date' |
233 | published_at: 'Publication date' | ||
234 | published_by: 'Published by' | ||
226 | new: | 235 | new: |
227 | page_title: 'Save new entry' | 236 | page_title: 'Save new entry' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'Edit an entry' | 243 | page_title: 'Edit an entry' |
235 | title_label: 'Title' | 244 | title_label: 'Title' |
236 | url_label: 'Url' | 245 | url_label: 'Url' |
237 | is_public_label: 'Public' | ||
238 | save_label: 'Save' | 246 | save_label: 'Save' |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 248 | shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | delete: "Are you sure you want to remove that article?" | ||
251 | delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'About' | 254 | page_title: 'About' |
@@ -510,6 +521,28 @@ user: | |||
510 | delete: Delete | 521 | delete: Delete |
511 | delete_confirm: Are you sure? | 522 | delete_confirm: Are you sure? |
512 | back_to_list: Back to list | 523 | back_to_list: Back to list |
524 | search: | ||
525 | placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | page_title: Site credentials management | ||
529 | new_site_credential: Create a credential | ||
530 | edit_site_credential: Edit an existing credential | ||
531 | description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | list: | ||
533 | actions: Actions | ||
534 | edit_action: Edit | ||
535 | yes: Yes | ||
536 | no: No | ||
537 | create_new_one: Create a new credential | ||
538 | form: | ||
539 | username_label: 'Username' | ||
540 | host_label: 'Host' | ||
541 | password_label: 'Password' | ||
542 | save: Save | ||
543 | delete: Delete | ||
544 | delete_confirm: Are you sure? | ||
545 | back_to_list: Back to list | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | page_title: An error occurred | 548 | page_title: An error occurred |
@@ -528,6 +561,7 @@ flashes: | |||
528 | annotations_reset: Annotations reset | 561 | annotations_reset: Annotations reset |
529 | tags_reset: Tags reset | 562 | tags_reset: Tags reset |
530 | entries_reset: Entries reset | 563 | entries_reset: Entries reset |
564 | archived_reset: Archived entries deleted | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'Entry already saved on %date%' | 567 | entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'User "%username%" added' | 596 | added: 'User "%username%" added' |
563 | updated: 'User "%username%" updated' | 597 | updated: 'User "%username%" updated' |
564 | deleted: 'User "%username%" deleted' | 598 | deleted: 'User "%username%" deleted' |
599 | site_credential: | ||
600 | notice: | ||
601 | added: 'Site credential for "%host%" added' | ||
602 | updated: 'Site credential for "%host%" updated' | ||
603 | deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml index 2f769b7e..96998f53 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: 'Guardar un enlace' | 32 | save_link: 'Guardar un enlace' |
33 | back_to_unread: 'Volver a los artículos sin leer' | 33 | back_to_unread: 'Volver a los artículos sin leer' |
34 | users_management: 'Configuración de usuarios' | 34 | users_management: 'Configuración de usuarios' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Añadir un nuevo artículo' | 37 | add_new_entry: 'Añadir un nuevo artículo' |
37 | search: 'Buscar' | 38 | search: 'Buscar' |
@@ -76,6 +77,7 @@ config: | |||
76 | redirect_current_page: 'A la página actual' | 77 | redirect_current_page: 'A la página actual' |
77 | pocket_consumer_key_label: Clave de consumidor para importar contenidos de Pocket | 78 | pocket_consumer_key_label: Clave de consumidor para importar contenidos de Pocket |
78 | android_configuration: Configura tu aplicación Android | 79 | android_configuration: Configura tu aplicación Android |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | help_theme: "wallabag es personalizable. Puedes elegir tu tema preferido aquí." | 81 | help_theme: "wallabag es personalizable. Puedes elegir tu tema preferido aquí." |
80 | help_items_per_page: "Puedes cambiar el número de artículos mostrados en cada página." | 82 | help_items_per_page: "Puedes cambiar el número de artículos mostrados en cada página." |
81 | help_reading_speed: "wallabag calcula un tiempo de lectura para cada artículo. Puedes definir aquí, gracias a esta lista, si eres un lector rápido o lento. wallabag recalculará el tiempo de lectura para cada artículo." | 83 | help_reading_speed: "wallabag calcula un tiempo de lectura para cada artículo. Puedes definir aquí, gracias a esta lista, si eres un lector rápido o lento. wallabag recalculará el tiempo de lectura para cada artículo." |
@@ -92,6 +94,7 @@ config: | |||
92 | unread: 'sin leer' | 94 | unread: 'sin leer' |
93 | starred: 'favoritos' | 95 | starred: 'favoritos' |
94 | archive: 'archivados' | 96 | archive: 'archivados' |
97 | # all: 'All' | ||
95 | rss_limit: 'Límite de artículos en feed RSS' | 98 | rss_limit: 'Límite de artículos en feed RSS' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "Con la autenticación en dos pasos recibirá código por e-mail en cada nueva conexión que no sea de confianza." | 100 | two_factor_description: "Con la autenticación en dos pasos recibirá código por e-mail en cada nueva conexión que no sea de confianza." |
@@ -110,6 +113,7 @@ config: | |||
110 | annotations: Eliminar TODAS las anotaciones | 113 | annotations: Eliminar TODAS las anotaciones |
111 | tags: Eliminar TODAS las etiquetas | 114 | tags: Eliminar TODAS las etiquetas |
112 | entries: Eliminar TODOS los artículos | 115 | entries: Eliminar TODOS los artículos |
116 | # archived: Remove ALL archived entries | ||
113 | confirm: ¿Estás completamente seguro? (NO SE PUEDE DESHACER) | 117 | confirm: ¿Estás completamente seguro? (NO SE PUEDE DESHACER) |
114 | form_password: | 118 | form_password: |
115 | description: "Puedes cambiar la contraseña aquí. Tu nueva contraseña debe tener al menos 8 caracteres." | 119 | description: "Puedes cambiar la contraseña aquí. Tu nueva contraseña debe tener al menos 8 caracteres." |
@@ -154,6 +158,7 @@ config: | |||
154 | or: 'Una regla U otra' | 158 | or: 'Una regla U otra' |
155 | and: 'Una regla Y la otra' | 159 | and: 'Una regla Y la otra' |
156 | matches: 'Prueba si un <i>sujeto</i> corresponde a una <i>búsqueda</i> (insensible a mayusculas).<br />Ejemplo : <code>title matches "fútbol"</code>' | 160 | matches: 'Prueba si un <i>sujeto</i> corresponde a una <i>búsqueda</i> (insensible a mayusculas).<br />Ejemplo : <code>title matches "fútbol"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Sin leer' | 190 | unread_label: 'Sin leer' |
186 | preview_picture_label: 'Tiene imagen de previsualización' | 191 | preview_picture_label: 'Tiene imagen de previsualización' |
187 | preview_picture_help: 'Imagen de previsualización' | 192 | preview_picture_help: 'Imagen de previsualización' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'Idioma' | 195 | language_label: 'Idioma' |
189 | http_status_label: 'Código de estado HTTP' | 196 | http_status_label: 'Código de estado HTTP' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} Sin anotaciones|{1} Una anotación|]1,Inf[ %count% anotaciones' | 231 | annotations_on_the_entry: '{0} Sin anotaciones|{1} Una anotación|]1,Inf[ %count% anotaciones' |
225 | created_at: 'Fecha de creación' | 232 | created_at: 'Fecha de creación' |
233 | # published_at: 'Publication date' | ||
234 | # published_by: 'Published by' | ||
226 | new: | 235 | new: |
227 | page_title: 'Guardar un nuevo artículo' | 236 | page_title: 'Guardar un nuevo artículo' |
228 | placeholder: 'http://sitioweb.com' | 237 | placeholder: 'http://sitioweb.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'Editar un artículo' | 243 | page_title: 'Editar un artículo' |
235 | title_label: 'Título' | 244 | title_label: 'Título' |
236 | url_label: 'URL' | 245 | url_label: 'URL' |
237 | is_public_label: 'Es público' | ||
238 | save_label: 'Guardar' | 246 | save_label: 'Guardar' |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "Este artículo se ha compartido con <a href='%wallabag_instance%'>wallabag</a>" | 248 | shared_by_wallabag: "Este artículo se ha compartido con <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | # delete: "Are you sure you want to remove that article?" | ||
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'Acerca de' | 254 | page_title: 'Acerca de' |
@@ -510,6 +521,28 @@ user: | |||
510 | delete: Eliminar | 521 | delete: Eliminar |
511 | delete_confirm: ¿Estás seguro? | 522 | delete_confirm: ¿Estás seguro? |
512 | back_to_list: Volver a la lista | 523 | back_to_list: Volver a la lista |
524 | search: | ||
525 | # placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | # list: | ||
533 | # actions: Actions | ||
534 | # edit_action: Edit | ||
535 | # yes: Yes | ||
536 | # no: No | ||
537 | # create_new_one: Create a new credential | ||
538 | # form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | # save: Save | ||
543 | # delete: Delete | ||
544 | # delete_confirm: Are you sure? | ||
545 | # back_to_list: Back to list | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | page_title: Ha ocurrido un error | 548 | page_title: Ha ocurrido un error |
@@ -528,6 +561,7 @@ flashes: | |||
528 | annotations_reset: Anotaciones reiniciadas | 561 | annotations_reset: Anotaciones reiniciadas |
529 | tags_reset: Etiquetas reiniciadas | 562 | tags_reset: Etiquetas reiniciadas |
530 | entries_reset: Artículos reiniciados | 563 | entries_reset: Artículos reiniciados |
564 | # archived_reset: Archived entries deleted | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'Artículo ya guardado el %fecha%' | 567 | entry_already_saved: 'Artículo ya guardado el %fecha%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'Añadido el usuario "%username%"' | 596 | added: 'Añadido el usuario "%username%"' |
563 | updated: 'Actualizado el usuario "%username%"' | 597 | updated: 'Actualizado el usuario "%username%"' |
564 | deleted: 'Eliminado el usuario "%username%"' | 598 | deleted: 'Eliminado el usuario "%username%"' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml index 647f12ad..57e6c029 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: 'ذخیرهٔ یک پیوند' | 32 | save_link: 'ذخیرهٔ یک پیوند' |
33 | back_to_unread: 'بازگشت به خواندهنشدهها' | 33 | back_to_unread: 'بازگشت به خواندهنشدهها' |
34 | # users_management: 'Users management' | 34 | # users_management: 'Users management' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'افزودن مقالهٔ تازه' | 37 | add_new_entry: 'افزودن مقالهٔ تازه' |
37 | search: 'جستجو' | 38 | search: 'جستجو' |
@@ -77,6 +78,7 @@ config: | |||
77 | pocket_consumer_key_label: کلید کاربری Pocket برای درونریزی مطالب | 78 | pocket_consumer_key_label: کلید کاربری Pocket برای درونریزی مطالب |
78 | # android_configuration: Configure your Android application | 79 | # android_configuration: Configure your Android application |
79 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." | 80 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." |
81 | # android_instruction: "Touch here to prefill your Android application" | ||
80 | # help_items_per_page: "You can change the number of articles displayed on each page." | 82 | # help_items_per_page: "You can change the number of articles displayed on each page." |
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." | 83 | # 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." |
82 | # help_language: "You can change the language of wallabag interface." | 84 | # help_language: "You can change the language of wallabag interface." |
@@ -92,6 +94,7 @@ config: | |||
92 | unread: 'خواندهنشده' | 94 | unread: 'خواندهنشده' |
93 | starred: 'برگزیده' | 95 | starred: 'برگزیده' |
94 | archive: 'بایگانی' | 96 | archive: 'بایگانی' |
97 | # all: 'All' | ||
95 | rss_limit: 'محدودیت آر-اس-اس' | 98 | rss_limit: 'محدودیت آر-اس-اس' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "با فعالکردن تأیید ۲مرحلهای هر بار که اتصال تأییدنشدهای برقرار شد، به شما یک کد از راه ایمیل فرستاده میشود" | 100 | two_factor_description: "با فعالکردن تأیید ۲مرحلهای هر بار که اتصال تأییدنشدهای برقرار شد، به شما یک کد از راه ایمیل فرستاده میشود" |
@@ -110,6 +113,7 @@ config: | |||
110 | # annotations: Remove ALL annotations | 113 | # annotations: Remove ALL annotations |
111 | # tags: Remove ALL tags | 114 | # tags: Remove ALL tags |
112 | # entries: Remove ALL entries | 115 | # entries: Remove ALL entries |
116 | # archived: Remove ALL archived entries | ||
113 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | 117 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) |
114 | form_password: | 118 | form_password: |
115 | # description: "You can change your password here. Your new password should by at least 8 characters long." | 119 | # description: "You can change your password here. Your new password should by at least 8 characters long." |
@@ -154,6 +158,7 @@ config: | |||
154 | # or: 'One rule OR another' | 158 | # or: 'One rule OR another' |
155 | # and: 'One rule AND another' | 159 | # and: 'One rule AND another' |
156 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' | 160 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'خواندهنشده' | 190 | unread_label: 'خواندهنشده' |
186 | preview_picture_label: 'دارای عکس پیشنمایش' | 191 | preview_picture_label: 'دارای عکس پیشنمایش' |
187 | preview_picture_help: 'پیشنمایش عکس' | 192 | preview_picture_help: 'پیشنمایش عکس' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'زبان' | 195 | language_label: 'زبان' |
189 | # http_status_label: 'HTTP status' | 196 | # http_status_label: 'HTTP status' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'اصلی' | 230 | original_article: 'اصلی' |
224 | annotations_on_the_entry: '{0} بدون حاشیه|{1} یک حاشیه|]1,Inf[ %nbحاشیه% annotations' | 231 | annotations_on_the_entry: '{0} بدون حاشیه|{1} یک حاشیه|]1,Inf[ %nbحاشیه% annotations' |
225 | created_at: 'زمان ساخت' | 232 | created_at: 'زمان ساخت' |
233 | # published_at: 'Publication date' | ||
234 | # published_by: 'Published by' | ||
226 | new: | 235 | new: |
227 | page_title: 'ذخیرهٔ مقالهٔ تازه' | 236 | page_title: 'ذخیرهٔ مقالهٔ تازه' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'ویرایش مقاله' | 243 | page_title: 'ویرایش مقاله' |
235 | title_label: 'عنوان' | 244 | title_label: 'عنوان' |
236 | url_label: 'نشانی' | 245 | url_label: 'نشانی' |
237 | is_public_label: 'عمومی' | ||
238 | save_label: 'ذخیره' | 246 | save_label: 'ذخیره' |
239 | public: | 247 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 248 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | # delete: "Are you sure you want to remove that article?" | ||
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'درباره' | 254 | page_title: 'درباره' |
@@ -510,6 +521,28 @@ user: | |||
510 | # delete: Delete | 521 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 522 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 523 | # back_to_list: Back to list |
524 | search: | ||
525 | # placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | # list: | ||
533 | # actions: Actions | ||
534 | # edit_action: Edit | ||
535 | # yes: Yes | ||
536 | # no: No | ||
537 | # create_new_one: Create a new credential | ||
538 | # form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | # save: Save | ||
543 | # delete: Delete | ||
544 | # delete_confirm: Are you sure? | ||
545 | # back_to_list: Back to list | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | # page_title: An error occurred | 548 | # page_title: An error occurred |
@@ -528,6 +561,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 561 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 562 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 563 | # entries_reset: Entries reset |
564 | # archived_reset: Archived entries deleted | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' | 567 | entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 596 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 597 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 598 | # deleted: 'User "%username%" deleted' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml index efddc46a..6eac4c36 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: "Sauvegarder un nouvel article" | 32 | save_link: "Sauvegarder un nouvel article" |
33 | back_to_unread: "Retour aux articles non lus" | 33 | back_to_unread: "Retour aux articles non lus" |
34 | users_management: "Gestion des utilisateurs" | 34 | users_management: "Gestion des utilisateurs" |
35 | site_credentials: 'Accès aux sites' | ||
35 | top: | 36 | top: |
36 | add_new_entry: "Sauvegarder un nouvel article" | 37 | add_new_entry: "Sauvegarder un nouvel article" |
37 | search: "Rechercher" | 38 | search: "Rechercher" |
@@ -46,7 +47,7 @@ footer: | |||
46 | social: "Social" | 47 | social: "Social" |
47 | powered_by: "propulsé par" | 48 | powered_by: "propulsé par" |
48 | about: "À propos" | 49 | about: "À propos" |
49 | stats: Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! | 50 | stats: "Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour !" |
50 | 51 | ||
51 | config: | 52 | config: |
52 | page_title: "Configuration" | 53 | page_title: "Configuration" |
@@ -71,27 +72,29 @@ config: | |||
71 | 300_word: "Je lis environ 300 mots par minute" | 72 | 300_word: "Je lis environ 300 mots par minute" |
72 | 400_word: "Je lis environ 400 mots par minute" | 73 | 400_word: "Je lis environ 400 mots par minute" |
73 | action_mark_as_read: | 74 | action_mark_as_read: |
74 | label: 'Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?' | 75 | label: "Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?" |
75 | redirect_homepage: "À la page d'accueil" | 76 | redirect_homepage: "À la page d’accueil" |
76 | redirect_current_page: 'À la page courante' | 77 | redirect_current_page: "À la page courante" |
77 | pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données | 78 | pocket_consumer_key_label: "Clé d’authentification Pocket pour importer les données" |
78 | android_configuration: Configurez votre application Android | 79 | android_configuration: "Configurez votre application Android" |
79 | help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez." | 80 | android_instruction: "Appuyez ici pour préremplir votre application Android" |
80 | help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page." | 81 | help_theme: "L’affichage de wallabag est personnalisable. C’est ici que vous choisissez le thème que vous préférez." |
82 | help_items_per_page: "Vous pouvez définir le nombre d’articles affichés sur chaque page." | ||
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." | 83 | 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." |
82 | help_language: "Vous pouvez définir la langue de l'interface de wallabag." | 84 | help_language: "Vous pouvez définir la langue de l’interface de wallabag." |
83 | help_pocket_consumer_key: "Nécessaire pour l'import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket." | 85 | help_pocket_consumer_key: "Nécessaire pour l’import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket." |
84 | form_rss: | 86 | form_rss: |
85 | description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d’abord créer un jeton." | 87 | description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d’abord créer un jeton." |
86 | token_label: "Jeton RSS" | 88 | token_label: "Jeton RSS" |
87 | no_token: "Aucun jeton généré" | 89 | no_token: "Aucun jeton généré" |
88 | token_create: "Créez votre jeton" | 90 | token_create: "Créez votre jeton" |
89 | token_reset: "Réinitialisez votre jeton" | 91 | token_reset: "Réinitialisez votre jeton" |
90 | rss_links: "Adresse de vos flux RSS" | 92 | rss_links: "Adresses de vos flux RSS" |
91 | rss_link: | 93 | rss_link: |
92 | unread: "non lus" | 94 | unread: "Non lus" |
93 | starred: "favoris" | 95 | starred: "Favoris" |
94 | archive: "lus" | 96 | archive: "Lus" |
97 | all: "Tous" | ||
95 | rss_limit: "Nombre d’articles dans le flux" | 98 | rss_limit: "Nombre d’articles dans le flux" |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "Activer l’authentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée." | 100 | two_factor_description: "Activer l’authentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée." |
@@ -100,17 +103,18 @@ config: | |||
100 | twoFactorAuthentication_label: "Double authentification" | 103 | twoFactorAuthentication_label: "Double authentification" |
101 | help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." | 104 | help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." |
102 | delete: | 105 | delete: |
103 | title: Supprimer mon compte (attention danger !) | 106 | 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é. | 107 | 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) | 108 | confirm: "Vous êtes vraiment sûr ? (C’EST IRRÉVERSIBLE)" |
106 | button: 'Supprimer mon compte' | 109 | button: "Supprimer mon compte" |
107 | reset: | 110 | reset: |
108 | title: Réinitialisation (attention danger !) | 111 | 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 ! | 112 | 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 | 113 | annotations: "Supprimer TOUTES les annotations" |
111 | tags: Supprimer TOUS les tags | 114 | tags: "Supprimer TOUS les tags" |
112 | entries: Supprimer TOUS les articles | 115 | entries: "Supprimer TOUS les articles" |
113 | confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) | 116 | archived: "Supprimer TOUS les articles archivés" |
117 | confirm: "Êtes-vous vraiment vraiment sûr ? (C’EST IRRÉVERSIBLE)" | ||
114 | form_password: | 118 | form_password: |
115 | description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." | 119 | description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." |
116 | old_password_label: "Mot de passe actuel" | 120 | old_password_label: "Mot de passe actuel" |
@@ -154,6 +158,7 @@ config: | |||
154 | or: "Une règle OU l’autre" | 158 | or: "Une règle OU l’autre" |
155 | and: "Une règle ET l’autre" | 159 | and: "Une règle ET l’autre" |
156 | matches: "Teste si un <i>sujet</i> correspond à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title matches \"football\"</code>" | 160 | matches: "Teste si un <i>sujet</i> correspond à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title matches \"football\"</code>" |
161 | notmatches: "Teste si un <i>sujet</i> ne correspond pas à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title notmatches \"football\"</code>" | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -162,7 +167,7 @@ entry: | |||
162 | archived: "Articles lus" | 167 | archived: "Articles lus" |
163 | filtered: "Articles filtrés" | 168 | filtered: "Articles filtrés" |
164 | filtered_tags: "Articles filtrés par tags :" | 169 | filtered_tags: "Articles filtrés par tags :" |
165 | filtered_search: 'Articles filtrés par recherche :' | 170 | filtered_search: "Articles filtrés par recherche :" |
166 | untagged: "Article sans tag" | 171 | untagged: "Article sans tag" |
167 | list: | 172 | list: |
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." | 173 | 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." |
@@ -185,8 +190,10 @@ entry: | |||
185 | unread_label: "Non lus" | 190 | unread_label: "Non lus" |
186 | preview_picture_label: "A une photo" | 191 | preview_picture_label: "A une photo" |
187 | preview_picture_help: "Photo" | 192 | preview_picture_help: "Photo" |
193 | is_public_label: 'A un lien public' | ||
194 | is_public_help: 'Lien public' | ||
188 | language_label: "Langue" | 195 | language_label: "Langue" |
189 | http_status_label: 'Statut HTTP' | 196 | http_status_label: "Statut HTTP" |
190 | reading_time: | 197 | reading_time: |
191 | label: "Durée de lecture en minutes" | 198 | label: "Durée de lecture en minutes" |
192 | from: "de" | 199 | from: "de" |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: "original" | 230 | original_article: "original" |
224 | annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations" | 231 | annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations" |
225 | created_at: "Date de création" | 232 | created_at: "Date de création" |
233 | published_at: "Date de publication" | ||
234 | published_by: "Publié par" | ||
226 | new: | 235 | new: |
227 | page_title: "Sauvegarder un nouvel article" | 236 | page_title: "Sauvegarder un nouvel article" |
228 | placeholder: "http://website.com" | 237 | placeholder: "http://website.com" |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: "Éditer un article" | 243 | page_title: "Éditer un article" |
235 | title_label: "Titre" | 244 | title_label: "Titre" |
236 | url_label: "Adresse" | 245 | url_label: "Adresse" |
237 | is_public_label: "Public" | ||
238 | save_label: "Enregistrer" | 246 | save_label: "Enregistrer" |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "Cet article a été partagé par <a href=\"%wallabag_instance%\">wallabag</a>" | 248 | shared_by_wallabag: "Cet article a été partagé par %username% avec <a href=\"%wallabag_instance%\">wallabag</a>" |
249 | confirm: | ||
250 | delete: "Voulez-vous vraiment supprimer cet article ?" | ||
251 | delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: "À propos" | 254 | page_title: "À propos" |
@@ -295,32 +306,32 @@ howto: | |||
295 | bookmarklet: | 306 | bookmarklet: |
296 | description: "Glissez et déposez ce lien dans votre barre de favoris :" | 307 | description: "Glissez et déposez ce lien dans votre barre de favoris :" |
297 | shortcuts: | 308 | shortcuts: |
298 | page_description: Voici les raccourcis disponibles dans wallabag. | 309 | page_description: "Voici les raccourcis disponibles dans wallabag." |
299 | shortcut: Raccourci | 310 | shortcut: "Raccourci" |
300 | action: Action | 311 | action: "Action" |
301 | all_pages_title: Raccourcis disponibles dans toutes les pages | 312 | all_pages_title: "Raccourcis disponibles dans toutes les pages" |
302 | go_unread: Afficher les articles non lus | 313 | go_unread: "Afficher les articles non lus" |
303 | go_starred: Afficher les articles favoris | 314 | go_starred: "Afficher les articles favoris" |
304 | go_archive: Afficher les articles lus | 315 | go_archive: "Afficher les articles lus" |
305 | go_all: Afficher tous les articles | 316 | go_all: "Afficher tous les articles" |
306 | go_tags: Afficher les tags | 317 | go_tags: "Afficher les tags" |
307 | go_config: Aller à la configuration | 318 | go_config: "Aller à la configuration" |
308 | go_import: Aller aux imports | 319 | go_import: "Aller aux imports" |
309 | go_developers: Aller à la section Développeurs | 320 | go_developers: "Aller à la section Développeurs" |
310 | go_howto: Afficher l'aide (cette page !) | 321 | go_howto: "Afficher l’aide (cette page !)" |
311 | go_logout: Se déconnecter | 322 | go_logout: "Se déconnecter" |
312 | list_title: Raccourcis disponibles dans les pages de liste | 323 | list_title: "Raccourcis disponibles dans les pages de liste" |
313 | search: Afficher le formulaire de recherche | 324 | search: "Afficher le formulaire de recherche" |
314 | article_title: Raccourcis disponibles quand on affiche un article | 325 | article_title: "Raccourcis disponibles quand on affiche un article" |
315 | open_original: Ouvrir l'URL originale de l'article | 326 | open_original: "Ouvrir l’URL originale de l’article" |
316 | toggle_favorite: Changer le statut Favori de l'article | 327 | toggle_favorite: "Changer le statut Favori de l’article" |
317 | toggle_archive: Changer le status Lu de l'article | 328 | toggle_archive: "Changer le status Lu de l’article" |
318 | delete: Supprimer l'article | 329 | delete: "Supprimer l’article" |
319 | material_title: Raccourcis disponibles avec le thème Material uniquement | 330 | material_title: "Raccourcis disponibles avec le thème Material uniquement" |
320 | add_link: Ajouter un nouvel article | 331 | add_link: "Ajouter un nouvel article" |
321 | hide_form: Masquer le formulaire courant (recherche ou nouvel article) | 332 | hide_form: "Masquer le formulaire courant (recherche ou nouvel article)" |
322 | arrows_navigation: Naviguer à travers les articles | 333 | arrows_navigation: "Naviguer à travers les articles" |
323 | open_article: Afficher l'article sélectionné | 334 | open_article: "Afficher l’article sélectionné" |
324 | 335 | ||
325 | quickstart: | 336 | quickstart: |
326 | page_title: "Pour bien débuter" | 337 | page_title: "Pour bien débuter" |
@@ -382,8 +393,8 @@ tag: | |||
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." | 393 | 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." |
383 | see_untagged_entries: "Voir les articles sans tag" | 394 | see_untagged_entries: "Voir les articles sans tag" |
384 | new: | 395 | new: |
385 | add: 'Ajouter' | 396 | add: "Ajouter" |
386 | placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.' | 397 | placeholder: "Vous pouvez ajouter plusieurs tags, séparés par une virgule." |
387 | 398 | ||
388 | import: | 399 | import: |
389 | page_title: "Importer" | 400 | page_title: "Importer" |
@@ -417,7 +428,7 @@ import: | |||
417 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." | 428 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." |
418 | worker: | 429 | worker: |
419 | enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" | 430 | 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." | 431 | 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." |
421 | firefox: | 432 | firefox: |
422 | page_title: "Import > Firefox" | 433 | page_title: "Import > Firefox" |
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>" | 434 | 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>" |
@@ -486,16 +497,16 @@ developer: | |||
486 | back: "Retour" | 497 | back: "Retour" |
487 | 498 | ||
488 | user: | 499 | user: |
489 | page_title: Gestion des utilisateurs | 500 | page_title: "Gestion des utilisateurs" |
490 | new_user: Créer un nouvel utilisateur | 501 | new_user: "Créer un nouvel utilisateur" |
491 | edit_user: Éditer un utilisateur existant | 502 | edit_user: "Éditer un utilisateur existant" |
492 | description: Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression) | 503 | description: "Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression)" |
493 | list: | 504 | list: |
494 | actions: Actions | 505 | actions: "Actions" |
495 | edit_action: Éditer | 506 | edit_action: "Éditer" |
496 | yes: Oui | 507 | yes: "Oui" |
497 | no: Non | 508 | no: "Non" |
498 | create_new_one: Créer un nouvel utilisateur | 509 | create_new_one: "Créer un nouvel utilisateur" |
499 | form: | 510 | form: |
500 | username_label: "Nom d’utilisateur" | 511 | username_label: "Nom d’utilisateur" |
501 | name_label: "Nom" | 512 | name_label: "Nom" |
@@ -508,11 +519,33 @@ user: | |||
508 | twofactor_label: "Double authentification" | 519 | twofactor_label: "Double authentification" |
509 | save: "Sauvegarder" | 520 | save: "Sauvegarder" |
510 | delete: "Supprimer" | 521 | delete: "Supprimer" |
511 | delete_confirm: "Voulez-vous vraiment ?" | 522 | delete_confirm: "Êtes-vous sûr ?" |
523 | back_to_list: "Revenir à la liste" | ||
524 | search: | ||
525 | placeholder: "Filtrer par nom d’utilisateur ou email" | ||
526 | |||
527 | site_credential: | ||
528 | page_title: Gestion des accès aux sites | ||
529 | new_site_credential: Créer un accès à un site | ||
530 | edit_site_credential: Éditer l'accès d'un site | ||
531 | description: "Ici vous pouvez gérer les accès aux différents sites. Ces accès permettent de récupérer des contenus sur des sites qui requièrent une authentification ou un paywall" | ||
532 | list: | ||
533 | actions: Actions | ||
534 | edit_action: Éditer | ||
535 | yes: Oui | ||
536 | no: Non | ||
537 | create_new_one: Créer un nouvel accès à un site | ||
538 | form: | ||
539 | username_label: 'Identifiant' | ||
540 | host_label: 'Domaine' | ||
541 | password_label: 'Mot de passe' | ||
542 | save: "Sauvegarder" | ||
543 | delete: "Supprimer" | ||
544 | delete_confirm: "Êtes-vous sûr ?" | ||
512 | back_to_list: "Revenir à la liste" | 545 | back_to_list: "Revenir à la liste" |
513 | 546 | ||
514 | error: | 547 | error: |
515 | page_title: Une erreur est survenue | 548 | page_title: "Une erreur est survenue" |
516 | 549 | ||
517 | flashes: | 550 | flashes: |
518 | config: | 551 | config: |
@@ -525,9 +558,10 @@ flashes: | |||
525 | tagging_rules_updated: "Règles mises à jour" | 558 | tagging_rules_updated: "Règles mises à jour" |
526 | tagging_rules_deleted: "Règle supprimée" | 559 | tagging_rules_deleted: "Règle supprimée" |
527 | rss_token_updated: "Jeton RSS mis à jour" | 560 | rss_token_updated: "Jeton RSS mis à jour" |
528 | annotations_reset: Annotations supprimées | 561 | annotations_reset: "Annotations supprimées" |
529 | tags_reset: Tags supprimés | 562 | tags_reset: "Tags supprimés" |
530 | entries_reset: Articles supprimés | 563 | entries_reset: "Articles supprimés" |
564 | archived_reset: "Articles archivés supprimés" | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: "Article déjà sauvegardé le %date%" | 567 | entry_already_saved: "Article déjà sauvegardé le %date%" |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'Utilisateur "%username%" ajouté' | 596 | added: 'Utilisateur "%username%" ajouté' |
563 | updated: 'Utilisateur "%username%" mis à jour' | 597 | updated: 'Utilisateur "%username%" mis à jour' |
564 | deleted: 'Utilisateur "%username%" supprimé' | 598 | deleted: 'Utilisateur "%username%" supprimé' |
599 | site_credential: | ||
600 | notice: | ||
601 | added: 'Accès au site "%host%" ajouté' | ||
602 | updated: 'Accès au site "%host%" mis à jour' | ||
603 | deleted: 'Accès au site "%host%" supprimé' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml index 3cd3fd17..fa7ae0b2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml | |||
@@ -91,9 +91,10 @@ config: | |||
91 | token_reset: 'Rigenera il tuo token' | 91 | token_reset: 'Rigenera il tuo token' |
92 | rss_links: 'Collegamenti RSS' | 92 | rss_links: 'Collegamenti RSS' |
93 | rss_link: | 93 | rss_link: |
94 | unread: 'non letti' | 94 | unread: 'Non letti' |
95 | starred: 'preferiti' | 95 | starred: 'Preferiti' |
96 | archive: 'archiviati' | 96 | archive: 'Archiviati' |
97 | # all: 'All' | ||
97 | rss_limit: 'Numero di elementi nel feed' | 98 | rss_limit: 'Numero di elementi nel feed' |
98 | form_user: | 99 | form_user: |
99 | two_factor_description: "Abilitando l'autenticazione a due fattori riceverai una e-mail con un codice per ogni nuova connesione non verificata" | 100 | two_factor_description: "Abilitando l'autenticazione a due fattori riceverai una e-mail con un codice per ogni nuova connesione non verificata" |
@@ -112,6 +113,7 @@ config: | |||
112 | annotations: Rimuovi TUTTE le annotazioni | 113 | annotations: Rimuovi TUTTE le annotazioni |
113 | tags: Rimuovi TUTTE le etichette | 114 | tags: Rimuovi TUTTE le etichette |
114 | entries: Rimuovi TUTTI gli articoli | 115 | entries: Rimuovi TUTTI gli articoli |
116 | # archived: Remove ALL archived entries | ||
115 | confirm: Sei veramente sicuro? (NON PUOI TORNARE INDIETRO) | 117 | confirm: Sei veramente sicuro? (NON PUOI TORNARE INDIETRO) |
116 | form_password: | 118 | form_password: |
117 | description: "Qui puoi cambiare la tua password. La tua nuova password dovrebbe essere composta da almeno 8 caratteri." | 119 | description: "Qui puoi cambiare la tua password. La tua nuova password dovrebbe essere composta da almeno 8 caratteri." |
@@ -156,6 +158,7 @@ config: | |||
156 | or: "Una regola O un'altra" | 158 | or: "Una regola O un'altra" |
157 | and: "Una regola E un'altra" | 159 | and: "Una regola E un'altra" |
158 | matches: 'Verifica che un <i>oggetto</i> risulti in una <i>ricerca</i> (case-insensitive).<br />Esempio: <code>titolo contiene "football"</code>' | 160 | matches: 'Verifica che un <i>oggetto</i> risulti in una <i>ricerca</i> (case-insensitive).<br />Esempio: <code>titolo contiene "football"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
159 | 162 | ||
160 | entry: | 163 | entry: |
161 | page_titles: | 164 | page_titles: |
@@ -187,6 +190,8 @@ entry: | |||
187 | unread_label: 'Non letti' | 190 | unread_label: 'Non letti' |
188 | preview_picture_label: "Ha un'immagine di anteprima" | 191 | preview_picture_label: "Ha un'immagine di anteprima" |
189 | preview_picture_help: 'Immagine di anteprima' | 192 | preview_picture_help: 'Immagine di anteprima' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
190 | language_label: 'Lingua' | 195 | language_label: 'Lingua' |
191 | http_status_label: 'Stato HTTP' | 196 | http_status_label: 'Stato HTTP' |
192 | reading_time: | 197 | reading_time: |
@@ -211,7 +216,7 @@ entry: | |||
211 | view_original_article: 'Contenuto originale' | 216 | view_original_article: 'Contenuto originale' |
212 | re_fetch_content: 'Ri-ottieni pagina' | 217 | re_fetch_content: 'Ri-ottieni pagina' |
213 | delete: 'Elimina' | 218 | delete: 'Elimina' |
214 | add_a_tag: 'Aggiungi un tag' | 219 | add_a_tag: 'Aggiungi un''etichetta' |
215 | share_content: 'Condividi' | 220 | share_content: 'Condividi' |
216 | share_email_label: 'E-mail' | 221 | share_email_label: 'E-mail' |
217 | public_link: 'Link pubblico' | 222 | public_link: 'Link pubblico' |
@@ -222,7 +227,7 @@ entry: | |||
222 | label: 'Problemi?' | 227 | label: 'Problemi?' |
223 | description: 'Questo contenuto viene visualizzato male?' | 228 | description: 'Questo contenuto viene visualizzato male?' |
224 | edit_title: 'Modifica titolo' | 229 | edit_title: 'Modifica titolo' |
225 | original_article: 'originale' | 230 | original_article: 'Originale' |
226 | annotations_on_the_entry: '{0} Nessuna annotazione|{1} Una annotazione|]1,Inf[ %count% annotazioni' | 231 | annotations_on_the_entry: '{0} Nessuna annotazione|{1} Una annotazione|]1,Inf[ %count% annotazioni' |
227 | created_at: 'Data di creazione' | 232 | created_at: 'Data di creazione' |
228 | published_at: 'Data di pubblicazione' | 233 | published_at: 'Data di pubblicazione' |
@@ -238,13 +243,15 @@ entry: | |||
238 | page_title: 'Modifica voce' | 243 | page_title: 'Modifica voce' |
239 | title_label: 'Titolo' | 244 | title_label: 'Titolo' |
240 | url_label: 'Url' | 245 | url_label: 'Url' |
241 | is_public_label: 'Pubblico' | ||
242 | save_label: 'Salva' | 246 | save_label: 'Salva' |
243 | public: | 247 | public: |
244 | shared_by_wallabag: "Questo articolo è stato condiviso da %username% con <a href='%wallabag_instance%'>wallabag</a>" | 248 | shared_by_wallabag: "Questo articolo è stato condiviso da %username% con <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | delete: "Vuoi veramente rimuovere quell'articolo?" | ||
251 | delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?" | ||
245 | 252 | ||
246 | about: | 253 | about: |
247 | page_title: 'About' | 254 | page_title: 'A proposito' |
248 | top_menu: | 255 | top_menu: |
249 | who_behind_wallabag: "Chi c'è dietro a wallabag" | 256 | who_behind_wallabag: "Chi c'è dietro a wallabag" |
250 | getting_help: 'Ottieni aiuto' | 257 | getting_help: 'Ottieni aiuto' |
@@ -263,7 +270,7 @@ about: | |||
263 | bug_reports: 'Bug reports' | 270 | bug_reports: 'Bug reports' |
264 | support: '<a href="https://github.com/wallabag/wallabag/issues">su GitHub</a>' | 271 | support: '<a href="https://github.com/wallabag/wallabag/issues">su GitHub</a>' |
265 | helping: | 272 | helping: |
266 | description: 'wallabag è gratuito opensource. Puoi aiutarci:' | 273 | description: 'wallabag è gratuito ed OpenSource. Puoi aiutarci:' |
267 | by_contributing: 'per contribuire al progetto:' | 274 | by_contributing: 'per contribuire al progetto:' |
268 | by_contributing_2: 'un elenco delle attività richieste' | 275 | by_contributing_2: 'un elenco delle attività richieste' |
269 | by_paypal: 'via Paypal' | 276 | by_paypal: 'via Paypal' |
@@ -331,7 +338,7 @@ quickstart: | |||
331 | more: 'Più…' | 338 | more: 'Più…' |
332 | intro: | 339 | intro: |
333 | title: 'Benvenuto su wallabag!' | 340 | title: 'Benvenuto su wallabag!' |
334 | paragraph_1: "Un tour in cui ti guideremo per scoprire e che ti mostrerà delle funzionalità che potrebbero interessarti." | 341 | paragraph_1: "Ti accompagneremo alla scoperta di wallabag e ti mostreremo delle funzionalità che potrebbero interessarti." |
335 | paragraph_2: 'Seguici!' | 342 | paragraph_2: 'Seguici!' |
336 | configure: | 343 | configure: |
337 | title: "Configura l'applicazione" | 344 | title: "Configura l'applicazione" |
@@ -401,20 +408,20 @@ import: | |||
401 | save_label: 'Carica file' | 408 | save_label: 'Carica file' |
402 | pocket: | 409 | pocket: |
403 | page_title: 'Importa da > Pocket' | 410 | page_title: 'Importa da > Pocket' |
404 | description: "Questo importatore copierà tutti i tuoi dati da Pocket. Pocket non ci consente di ottenere contenuti dal loro servzio, così il contenuto leggibile di ogni articolo verrà ri-ottenuto da wallabag." | 411 | description: "Questo importatore copierà tutti i tuoi dati da Pocket. Pocket non ci consente di ottenere contenuti dal loro servizio, così il contenuto leggibile di ogni articolo verrà ri-ottenuto da wallabag." |
405 | config_missing: | 412 | config_missing: |
406 | description: "Importazione da Pocket non configurata." | 413 | description: "Importazione da Pocket non configurata." |
407 | admin_message: 'Devi definire %keyurls% una pocket_consumer_key %keyurle%.' | 414 | admin_message: 'Devi definire %keyurls% una pocket_consumer_key %keyurle%.' |
408 | user_message: 'Il tuo amministratore di server deve define una API Key per Pocket.' | 415 | user_message: 'Il tuo amministratore del server deve definire una API Key per Pocket.' |
409 | authorize_message: 'Puoi importare dati dal tuo account Pocket. Devi solo cliccare sul pulsante sottostante e autorizzare la connessione a getpocket.com.' | 416 | authorize_message: 'Puoi importare dati dal tuo account Pocket. Devi solo cliccare sul pulsante sottostante e autorizzare la connessione a getpocket.com.' |
410 | connect_to_pocket: 'Connetti a Pocket and importa i dati' | 417 | connect_to_pocket: 'Connetti a Pocket and importa i dati' |
411 | wallabag_v1: | 418 | wallabag_v1: |
412 | page_title: 'Importa da > Wallabag v1' | 419 | page_title: 'Importa da > Wallabag v1' |
413 | description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v1. Nella tua pagina di configurazione, clicca su "JSON export" nella sezione "Esport i tuoi dati di wallabag". Otterrai un file "wallabag-export-1-xxxx-xx-xx.json".' | 420 | description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v1. Nella tua pagina di configurazione, clicca su "JSON export" nella sezione "Esporta i tuoi dati di wallabag". Otterrai un file "wallabag-export-1-xxxx-xx-xx.json".' |
414 | how_to: 'Seleziona la tua esportazione di wallabag e clicca sul pulsante sottostante caricare il file e importare i dati.' | 421 | how_to: 'Seleziona la tua esportazione di wallabag e clicca sul pulsante sottostante caricare il file e importare i dati.' |
415 | wallabag_v2: | 422 | wallabag_v2: |
416 | page_title: 'Importa da > Wallabag v2' | 423 | page_title: 'Importa da > Wallabag v2' |
417 | description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v2. Vai in "Tutti i contenuti", e, nella sidebar di esportazione, clicca su "JSON". Otterrai un file "Tutti i contenuti.json".' | 424 | description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v2. Vai in "Tutti i contenuti", e, nella barra laterale di esportazione, clicca su "JSON". Otterrai un file "Tutti i contenuti.json' |
418 | readability: | 425 | readability: |
419 | page_title: 'Importa da > Readability' | 426 | page_title: 'Importa da > Readability' |
420 | description: 'Questo importatore copierà tutti i tuoi articoli da Readability. Nella pagina strumenti (https://www.readability.com/tools/), clicca su "Export your data" nella sezione "Data Export". Riceverai una E-mail per scaricare un file json (che tuttavia non termina con .json).' | 427 | description: 'Questo importatore copierà tutti i tuoi articoli da Readability. Nella pagina strumenti (https://www.readability.com/tools/), clicca su "Export your data" nella sezione "Data Export". Riceverai una E-mail per scaricare un file json (che tuttavia non termina con .json).' |
@@ -514,6 +521,28 @@ user: | |||
514 | delete: Cancella | 521 | delete: Cancella |
515 | delete_confirm: Sei sicuro? | 522 | delete_confirm: Sei sicuro? |
516 | back_to_list: Torna alla lista | 523 | back_to_list: Torna alla lista |
524 | search: | ||
525 | # placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | # list: | ||
533 | # actions: Actions | ||
534 | # edit_action: Edit | ||
535 | # yes: Yes | ||
536 | # no: No | ||
537 | # create_new_one: Create a new credential | ||
538 | # form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | # save: Save | ||
543 | # delete: Delete | ||
544 | # delete_confirm: Are you sure? | ||
545 | # back_to_list: Back to list | ||
517 | 546 | ||
518 | error: | 547 | error: |
519 | page_title: Si è verificato un errore | 548 | page_title: Si è verificato un errore |
@@ -532,6 +561,7 @@ flashes: | |||
532 | annotations_reset: Reset annotazioni | 561 | annotations_reset: Reset annotazioni |
533 | tags_reset: Reset etichette | 562 | tags_reset: Reset etichette |
534 | entries_reset: Reset articoli | 563 | entries_reset: Reset articoli |
564 | # archived_reset: Archived entries deleted | ||
535 | entry: | 565 | entry: |
536 | notice: | 566 | notice: |
537 | entry_already_saved: 'Contenuto già salvato in data %date%' | 567 | entry_already_saved: 'Contenuto già salvato in data %date%' |
@@ -566,3 +596,8 @@ flashes: | |||
566 | added: 'Utente "%username%" aggiunto' | 596 | added: 'Utente "%username%" aggiunto' |
567 | updated: 'Utente "%username%" aggiornato' | 597 | updated: 'Utente "%username%" aggiornato' |
568 | deleted: 'Utente "%username%" eliminato' | 598 | deleted: 'Utente "%username%" eliminato' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml index 913e3bcb..be57e903 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
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: 'Gestion dels utilizaires' | 34 | users_management: 'Gestion dels utilizaires' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Enregistrar un novèl article' | 37 | add_new_entry: 'Enregistrar un novèl article' |
37 | search: 'Cercar' | 38 | search: 'Cercar' |
@@ -76,6 +77,7 @@ config: | |||
76 | redirect_current_page: 'A la pagina actuala' | 77 | redirect_current_page: 'A la pagina actuala' |
77 | pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas | 78 | pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas |
78 | android_configuration: Configuratz vòstra aplicacion Android | 79 | android_configuration: Configuratz vòstra aplicacion Android |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | help_theme: "wallabag es personalizable. Podètz causir vòstre tèma preferit aquí." | 81 | help_theme: "wallabag es personalizable. Podètz causir vòstre tèma preferit aquí." |
80 | help_items_per_page: "Podètz cambiar lo nombre d'articles afichats per pagina." | 82 | help_items_per_page: "Podètz cambiar lo nombre d'articles afichats per pagina." |
81 | help_reading_speed: "wallabag calcula lo temps de lectura per cada article. Podètz lo definir aquí, gràcias a aquesta lista, se sètz un legeire rapid o lent. wallabag tornarà calcular lo temps de lectura per cada article." | 83 | help_reading_speed: "wallabag calcula lo temps de lectura per cada article. Podètz lo definir aquí, gràcias a aquesta lista, se sètz un legeire rapid o lent. wallabag tornarà calcular lo temps de lectura per cada article." |
@@ -87,11 +89,12 @@ config: | |||
87 | no_token: 'Pas cap de geton generat' | 89 | no_token: 'Pas cap de geton generat' |
88 | token_create: 'Creatz vòstre geton' | 90 | token_create: 'Creatz vòstre geton' |
89 | token_reset: 'Reïnicializatz vòstre geton' | 91 | token_reset: 'Reïnicializatz vòstre geton' |
90 | rss_links: 'URL de vòstres fluxes RSS' | 92 | rss_links: 'URLs de vòstres fluxes RSS' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'pas legits' | 94 | unread: 'Pas legits' |
93 | starred: 'favorits' | 95 | starred: 'Favorits' |
94 | archive: 'legits' | 96 | archive: 'Legits' |
97 | # all: 'All' | ||
95 | rss_limit: "Nombre d'articles dins un flux RSS" | 98 | rss_limit: "Nombre d'articles dins un flux RSS" |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "Activar l'autentificacion doble-factor vòl dire que recebretz un còdi per corrièl per cada novèla connexion pas aprovada." | 100 | two_factor_description: "Activar l'autentificacion doble-factor vòl dire que recebretz un còdi per corrièl per cada novèla connexion pas aprovada." |
@@ -110,6 +113,7 @@ config: | |||
110 | annotations: Levar TOTAS las anotacions | 113 | annotations: Levar TOTAS las anotacions |
111 | tags: Levar TOTAS las etiquetas | 114 | tags: Levar TOTAS las etiquetas |
112 | entries: Levar TOTES los articles | 115 | entries: Levar TOTES los articles |
116 | archived: Levar TOTES los articles archivats | ||
113 | confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE) | 117 | confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE) |
114 | form_password: | 118 | form_password: |
115 | description: "Podètz cambiar vòstre senhal aquí. Vòstre senhal deu èsser long d'almens 8 caractèrs." | 119 | description: "Podètz cambiar vòstre senhal aquí. Vòstre senhal deu èsser long d'almens 8 caractèrs." |
@@ -153,7 +157,8 @@ config: | |||
153 | not_equal_to: 'Diferent de…' | 157 | not_equal_to: 'Diferent de…' |
154 | or: "Una règla O l'autra" | 158 | or: "Una règla O l'autra" |
155 | and: "Una règla E l'autra" | 159 | and: "Una règla E l'autra" |
156 | matches: 'Teste se un <i>subjècte</i> correspond a una <i>recerca</i> (non sensibla a la cassa).<br />Exemple : <code>title matches \"football\"</code>' | 160 | matches: 'Teste se un <i>subjècte</i> correspond a una <i>recèrca</i> (non sensibla a la cassa).<br />Exemple : <code>title matches \"football\"</code>' |
161 | notmatches: 'Teste se <i>subjècte</i> correspond pas a una <i>recèrca</i> (sensibla a la cassa).<br />Example : <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -183,8 +188,10 @@ entry: | |||
183 | archived_label: 'Legits' | 188 | archived_label: 'Legits' |
184 | starred_label: 'Favorits' | 189 | starred_label: 'Favorits' |
185 | unread_label: 'Pas legits' | 190 | unread_label: 'Pas legits' |
186 | preview_picture_label: 'A una fotò' | 191 | preview_picture_label: 'A un imatge' |
187 | preview_picture_help: 'Fotò' | 192 | preview_picture_help: 'Imatge' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'Lenga' | 195 | language_label: 'Lenga' |
189 | http_status_label: 'Estatut HTTP' | 196 | http_status_label: 'Estatut HTTP' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | annotations_on_the_entry: "{0} Pas cap d'anotacion|{1} Una anotacion|]1,Inf[ %count% anotacions" | 231 | annotations_on_the_entry: "{0} Pas cap d'anotacion|{1} Una anotacion|]1,Inf[ %count% anotacions" |
225 | created_at: 'Data de creacion' | 232 | created_at: 'Data de creacion' |
233 | published_at: 'Data de publicacion' | ||
234 | published_by: 'Publicat per' | ||
226 | new: | 235 | new: |
227 | page_title: 'Enregistrar un novèl article' | 236 | page_title: 'Enregistrar un novèl article' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'Modificar un article' | 243 | page_title: 'Modificar un article' |
235 | title_label: 'Títol' | 244 | title_label: 'Títol' |
236 | url_label: 'Url' | 245 | url_label: 'Url' |
237 | is_public_label: 'Public' | ||
238 | save_label: 'Enregistrar' | 246 | save_label: 'Enregistrar' |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>" | 248 | shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | # delete: "Are you sure you want to remove that article?" | ||
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'A prepaus' | 254 | page_title: 'A prepaus' |
@@ -341,8 +352,8 @@ quickstart: | |||
341 | new_user: 'Crear un novèl utilizaire' | 352 | new_user: 'Crear un novèl utilizaire' |
342 | analytics: 'Configurar las estadisticas' | 353 | analytics: 'Configurar las estadisticas' |
343 | sharing: 'Activar de paramètres de partatge' | 354 | sharing: 'Activar de paramètres de partatge' |
344 | export: 'Configurar los expòrt' | 355 | export: 'Configurar los expòrts' |
345 | import: 'Configurar los impòrt' | 356 | import: 'Configurar los impòrts' |
346 | first_steps: | 357 | first_steps: |
347 | title: 'Primièrs passes' | 358 | title: 'Primièrs passes' |
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." | 359 | 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." |
@@ -458,7 +469,7 @@ developer: | |||
458 | action: 'Suprimir aqueste client' | 469 | action: 'Suprimir aqueste client' |
459 | client: | 470 | client: |
460 | page_title: 'Gestion dels clients API > Novèl client' | 471 | page_title: 'Gestion dels clients API > Novèl client' |
461 | page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." | 472 | page_description: "Anatz crear un novèl client. Mercés de garnir l'url de redireccion cap a vòstra aplicacion." |
462 | form: | 473 | form: |
463 | name_label: "Nom del client" | 474 | name_label: "Nom del client" |
464 | redirect_uris_label: 'URLs de redireccion' | 475 | redirect_uris_label: 'URLs de redireccion' |
@@ -469,7 +480,7 @@ developer: | |||
469 | page_description: 'Vaquí los paramètres de vòstre client.' | 480 | page_description: 'Vaquí los paramètres de vòstre client.' |
470 | field_name: 'Nom del client' | 481 | field_name: 'Nom del client' |
471 | field_id: 'ID Client' | 482 | field_id: 'ID Client' |
472 | field_secret: 'Clau secreta' | 483 | field_secret: 'Clau secrèta' |
473 | back: 'Retour' | 484 | back: 'Retour' |
474 | read_howto: 'Legir "cossí crear ma primièra aplicacion"' | 485 | read_howto: 'Legir "cossí crear ma primièra aplicacion"' |
475 | howto: | 486 | howto: |
@@ -510,6 +521,28 @@ user: | |||
510 | delete: 'Suprimir' | 521 | delete: 'Suprimir' |
511 | delete_confirm: 'Sètz segur ?' | 522 | delete_confirm: 'Sètz segur ?' |
512 | back_to_list: 'Tornar a la lista' | 523 | back_to_list: 'Tornar a la lista' |
524 | search: | ||
525 | placeholder: "Filtrar per nom d'utilizaire o corrièl" | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | list: | ||
533 | actions: 'Accions' | ||
534 | edit_action: 'Modificar' | ||
535 | yes: 'Òc' | ||
536 | no: 'Non' | ||
537 | # create_new_one: Create a new credential | ||
538 | form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | save: 'Enregistrar' | ||
543 | delete: 'Suprimir' | ||
544 | delete_confirm: 'Sètz segur ?' | ||
545 | back_to_list: 'Tornar a la lista' | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | page_title: Una error s'es produsida | 548 | page_title: Una error s'es produsida |
@@ -528,6 +561,7 @@ flashes: | |||
528 | annotations_reset: Anotacions levadas | 561 | annotations_reset: Anotacions levadas |
529 | tags_reset: Etiquetas levadas | 562 | tags_reset: Etiquetas levadas |
530 | entries_reset: Articles levats | 563 | entries_reset: Articles levats |
564 | archived_reset: Articles archivat suprimits | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'Article ja salvargardat lo %date%' | 567 | entry_already_saved: 'Article ja salvargardat lo %date%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'Utilizaire "%username%" ajustat' | 596 | added: 'Utilizaire "%username%" ajustat' |
563 | updated: 'Utilizaire "%username%" mes a jorn' | 597 | updated: 'Utilizaire "%username%" mes a jorn' |
564 | deleted: 'Utilizaire "%username%" suprimit' | 598 | deleted: 'Utilizaire "%username%" suprimit' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index b990a6b9..00c559ed 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: 'Zapisz link' | 32 | save_link: 'Zapisz link' |
33 | back_to_unread: 'Powrót do nieprzeczytanych artykułów' | 33 | back_to_unread: 'Powrót do nieprzeczytanych artykułów' |
34 | users_management: 'Zarządzanie użytkownikami' | 34 | users_management: 'Zarządzanie użytkownikami' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Dodaj nowy wpis' | 37 | add_new_entry: 'Dodaj nowy wpis' |
37 | search: 'Szukaj' | 38 | search: 'Szukaj' |
@@ -76,6 +77,7 @@ config: | |||
76 | redirect_current_page: 'do bieżącej strony' | 77 | redirect_current_page: 'do bieżącej strony' |
77 | pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości' | 78 | pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości' |
78 | android_configuration: Skonfiguruj swoją androidową aplikację | 79 | android_configuration: Skonfiguruj swoją androidową aplikację |
80 | android_instruction: "Dotknij tutaj, aby wstępnie uzupełnij androidową aplikację" | ||
79 | help_theme: "Dopasuj wallabag do swoich potrzeb. Tutaj możesz wybrać preferowany przez ciebie motyw." | 81 | help_theme: "Dopasuj wallabag do swoich potrzeb. Tutaj możesz wybrać preferowany przez ciebie motyw." |
80 | help_items_per_page: "Możesz zmienić ilość artykułów wyświetlanych na każdej stronie." | 82 | help_items_per_page: "Możesz zmienić ilość artykułów wyświetlanych na każdej stronie." |
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." | 83 | 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." |
@@ -89,9 +91,10 @@ config: | |||
89 | token_reset: 'Zresetuj swojego tokena' | 91 | token_reset: 'Zresetuj swojego tokena' |
90 | rss_links: 'RSS links' | 92 | rss_links: 'RSS links' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'nieprzeczytane' | 94 | unread: 'Nieprzeczytane' |
93 | starred: 'oznaczone gwiazdką' | 95 | starred: 'Oznaczone gwiazdką' |
94 | archive: 'archiwum' | 96 | archive: 'Archiwum' |
97 | # all: 'All' | ||
95 | rss_limit: 'Link do RSS' | 98 | rss_limit: 'Link do RSS' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "Włączenie autoryzacji dwuetapowej oznacza, że będziesz otrzymywał maile z kodem przy każdym nowym, niezaufanym połączeniu" | 100 | two_factor_description: "Włączenie autoryzacji dwuetapowej oznacza, że będziesz otrzymywał maile z kodem przy każdym nowym, niezaufanym połączeniu" |
@@ -110,6 +113,7 @@ config: | |||
110 | annotations: Usuń WSZYSTKIE adnotacje | 113 | annotations: Usuń WSZYSTKIE adnotacje |
111 | tags: Usuń WSZYSTKIE tagi | 114 | tags: Usuń WSZYSTKIE tagi |
112 | entries: usuń WSZYTSTKIE wpisy | 115 | entries: usuń WSZYTSTKIE wpisy |
116 | archived: usuń WSZYSTKIE zarchiwizowane wpisy | ||
113 | confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć) | 117 | confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć) |
114 | form_password: | 118 | form_password: |
115 | description: "Tutaj możesz zmienić swoje hasło. Twoje nowe hasło powinno mieć conajmniej 8 znaków." | 119 | description: "Tutaj możesz zmienić swoje hasło. Twoje nowe hasło powinno mieć conajmniej 8 znaków." |
@@ -154,6 +158,7 @@ config: | |||
154 | or: 'Jedna reguła LUB inna' | 158 | or: 'Jedna reguła LUB inna' |
155 | and: 'Jedna reguła I inna' | 159 | and: 'Jedna reguła I inna' |
156 | matches: 'Sprawdź czy <i>temat</i> pasuje <i>szukaj</i> (duże lub małe litery).<br />Przykład: <code>tytuł zawiera "piłka nożna"</code>' | 160 | matches: 'Sprawdź czy <i>temat</i> pasuje <i>szukaj</i> (duże lub małe litery).<br />Przykład: <code>tytuł zawiera "piłka nożna"</code>' |
161 | notmatches: 'Sprawdź czy <i>temat</i> nie zawiera <i>szukaj</i> (duże lub małe litery).<br />Przykład: <code>tytuł nie zawiera "piłka nożna"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Nieprzeczytane' | 190 | unread_label: 'Nieprzeczytane' |
186 | preview_picture_label: 'Posiada podgląd obrazu' | 191 | preview_picture_label: 'Posiada podgląd obrazu' |
187 | preview_picture_help: 'Podgląd obrazu' | 192 | preview_picture_help: 'Podgląd obrazu' |
193 | is_public_label: 'Posiada publiczny link' | ||
194 | is_public_help: 'Publiczny link' | ||
188 | language_label: 'Język' | 195 | language_label: 'Język' |
189 | http_status_label: 'Status HTTP' | 196 | http_status_label: 'Status HTTP' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'oryginalny' | 230 | original_article: 'oryginalny' |
224 | annotations_on_the_entry: '{0} Nie ma adnotacji |{1} Jedna adnotacja |]1,Inf[ %count% adnotacji' | 231 | annotations_on_the_entry: '{0} Nie ma adnotacji |{1} Jedna adnotacja |]1,Inf[ %count% adnotacji' |
225 | created_at: 'Czas stworzenia' | 232 | created_at: 'Czas stworzenia' |
233 | published_at: 'Data publikacji' | ||
234 | published_by: 'Opublikowane przez' | ||
226 | new: | 235 | new: |
227 | page_title: 'Zapisz nowy wpis' | 236 | page_title: 'Zapisz nowy wpis' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'Edytuj wpis' | 243 | page_title: 'Edytuj wpis' |
235 | title_label: 'Tytuł' | 244 | title_label: 'Tytuł' |
236 | url_label: 'Adres URL' | 245 | url_label: 'Adres URL' |
237 | is_public_label: 'Publiczny' | ||
238 | save_label: 'Zapisz' | 246 | save_label: 'Zapisz' |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "Ten artykuł został udostępniony przez <a href='%wallabag_instance%'>wallabag</a>" | 248 | shared_by_wallabag: "Ten artykuł został udostępniony przez <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?" | ||
251 | delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'O nas' | 254 | page_title: 'O nas' |
@@ -510,6 +521,28 @@ user: | |||
510 | delete: Usuń | 521 | delete: Usuń |
511 | delete_confirm: Jesteś pewien? | 522 | delete_confirm: Jesteś pewien? |
512 | back_to_list: Powrót do listy | 523 | back_to_list: Powrót do listy |
524 | search: | ||
525 | placeholder: Filtruj po nazwie użytkownika lub adresie e-mail | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | list: | ||
533 | actions: Akcje | ||
534 | edit_action: Edytuj | ||
535 | yes: Tak | ||
536 | no: Nie | ||
537 | # create_new_one: Create a new credential | ||
538 | form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | save: Zapisz | ||
543 | delete: Usuń | ||
544 | delete_confirm: Jesteś pewien? | ||
545 | back_to_list: Powrót do listy | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | page_title: Wystąpił błąd | 548 | page_title: Wystąpił błąd |
@@ -528,6 +561,7 @@ flashes: | |||
528 | annotations_reset: Zresetuj adnotacje | 561 | annotations_reset: Zresetuj adnotacje |
529 | tags_reset: Zresetuj tagi | 562 | tags_reset: Zresetuj tagi |
530 | entries_reset: Zresetuj wpisy | 563 | entries_reset: Zresetuj wpisy |
564 | archived_reset: Zarchiwizowane wpisy usunięte | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'Wpis już został dodany %date%' | 567 | entry_already_saved: 'Wpis już został dodany %date%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'Użytkownik "%username%" dodany' | 596 | added: 'Użytkownik "%username%" dodany' |
563 | updated: 'Użytkownik "%username%" zaktualizowany' | 597 | updated: 'Użytkownik "%username%" zaktualizowany' |
564 | deleted: 'Użytkownik "%username%" usunięty' | 598 | deleted: 'Użytkownik "%username%" usunięty' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml index 3b1f9cb6..4ab5f144 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | save_link: 'Salvar um link' | 32 | save_link: 'Salvar um link' |
33 | back_to_unread: 'Voltar para os artigos não lidos' | 33 | back_to_unread: 'Voltar para os artigos não lidos' |
34 | users_management: 'Gestão de Usuários' | 34 | users_management: 'Gestão de Usuários' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Adicionar uma nova entrada' | 37 | add_new_entry: 'Adicionar uma nova entrada' |
37 | search: 'Pesquisa' | 38 | search: 'Pesquisa' |
@@ -76,6 +77,7 @@ config: | |||
76 | # redirect_current_page: 'To the current page' | 77 | # redirect_current_page: 'To the current page' |
77 | pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo' | 78 | pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo' |
78 | # android_configuration: Configure your Android application | 79 | # android_configuration: Configure your Android application |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." | 81 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." |
80 | # help_items_per_page: "You can change the number of articles displayed on each page." | 82 | # help_items_per_page: "You can change the number of articles displayed on each page." |
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." | 83 | # 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." |
@@ -89,9 +91,10 @@ config: | |||
89 | token_reset: 'Gerar novamente seu token' | 91 | token_reset: 'Gerar novamente seu token' |
90 | rss_links: 'Links RSS' | 92 | rss_links: 'Links RSS' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'não lido' | 94 | unread: 'Não lido' |
93 | starred: 'destacado' | 95 | starred: 'Destacado' |
94 | archive: 'arquivado' | 96 | archive: 'Arquivado' |
97 | # all: 'All' | ||
95 | rss_limit: 'Número de itens no feed' | 98 | rss_limit: 'Número de itens no feed' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: 'Habilitar autenticação de dois passos significa que você receberá um e-mail com um código a cada nova conexão desconhecida.' | 100 | two_factor_description: 'Habilitar autenticação de dois passos significa que você receberá um e-mail com um código a cada nova conexão desconhecida.' |
@@ -110,6 +113,7 @@ config: | |||
110 | # annotations: Remove ALL annotations | 113 | # annotations: Remove ALL annotations |
111 | # tags: Remove ALL tags | 114 | # tags: Remove ALL tags |
112 | # entries: Remove ALL entries | 115 | # entries: Remove ALL entries |
116 | # archived: Remove ALL archived entries | ||
113 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | 117 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) |
114 | form_password: | 118 | form_password: |
115 | # description: "You can change your password here. Your new password should by at least 8 characters long." | 119 | # description: "You can change your password here. Your new password should by at least 8 characters long." |
@@ -154,6 +158,7 @@ config: | |||
154 | or: 'Uma regra OU outra' | 158 | or: 'Uma regra OU outra' |
155 | and: 'Uma regra E outra' | 159 | and: 'Uma regra E outra' |
156 | matches: 'Testa que um <i>assunto</i> corresponde a uma <i>pesquisa</i> (maiúscula ou minúscula).<br />Exemplo: <code>título corresponde a "futebol"</code>' | 160 | matches: 'Testa que um <i>assunto</i> corresponde a uma <i>pesquisa</i> (maiúscula ou minúscula).<br />Exemplo: <code>título corresponde a "futebol"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Não Lido' | 190 | unread_label: 'Não Lido' |
186 | preview_picture_label: 'Possui uma imagem de preview' | 191 | preview_picture_label: 'Possui uma imagem de preview' |
187 | preview_picture_help: 'Imagem de preview' | 192 | preview_picture_help: 'Imagem de preview' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'Idioma' | 195 | language_label: 'Idioma' |
189 | # http_status_label: 'HTTP status' | 196 | # http_status_label: 'HTTP status' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações' | 231 | annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações' |
225 | created_at: 'Data de criação' | 232 | created_at: 'Data de criação' |
233 | # published_at: 'Publication date' | ||
234 | # published_by: 'Published by' | ||
226 | new: | 235 | new: |
227 | page_title: 'Salvar nova entrada' | 236 | page_title: 'Salvar nova entrada' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | page_title: 'Editar uma entrada' | 243 | page_title: 'Editar uma entrada' |
235 | title_label: 'Título' | 244 | title_label: 'Título' |
236 | url_label: 'Url' | 245 | url_label: 'Url' |
237 | is_public_label: 'Público' | ||
238 | save_label: 'Salvar' | 246 | save_label: 'Salvar' |
239 | public: | 247 | public: |
240 | shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>" | 248 | shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | # delete: "Are you sure you want to remove that article?" | ||
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'Sobre' | 254 | page_title: 'Sobre' |
@@ -510,6 +521,28 @@ user: | |||
510 | delete: 'Apagar' | 521 | delete: 'Apagar' |
511 | delete_confirm: 'Tem certeza?' | 522 | delete_confirm: 'Tem certeza?' |
512 | back_to_list: 'Voltar para a lista' | 523 | back_to_list: 'Voltar para a lista' |
524 | search: | ||
525 | # placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | list: | ||
533 | actions: 'Ações' | ||
534 | edit_action: 'Editar' | ||
535 | yes: 'Sim' | ||
536 | no: 'Não' | ||
537 | # create_new_one: Create a new credential | ||
538 | form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | save: 'Salvar' | ||
543 | delete: 'Apagar' | ||
544 | delete_confirm: 'Tem certeza?' | ||
545 | back_to_list: 'Voltar para a lista' | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | # page_title: An error occurred | 548 | # page_title: An error occurred |
@@ -528,6 +561,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 561 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 562 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 563 | # entries_reset: Entries reset |
564 | # archived_reset: Archived entries deleted | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | entry_already_saved: 'Entrada já foi salva em %date%' | 567 | entry_already_saved: 'Entrada já foi salva em %date%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | added: 'Usuário "%username%" adicionado' | 596 | added: 'Usuário "%username%" adicionado' |
563 | updated: 'Usuário "%username%" atualizado' | 597 | updated: 'Usuário "%username%" atualizado' |
564 | deleted: 'Usuário "%username%" removido' | 598 | deleted: 'Usuário "%username%" removido' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml index 728eed58..f16504ed 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | # save_link: 'Save a link' | 32 | # save_link: 'Save a link' |
33 | back_to_unread: 'Înapoi la articolele necitite' | 33 | back_to_unread: 'Înapoi la articolele necitite' |
34 | # users_management: 'Users management' | 34 | # users_management: 'Users management' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Introdu un nou articol' | 37 | add_new_entry: 'Introdu un nou articol' |
37 | search: 'Căutare' | 38 | search: 'Căutare' |
@@ -76,6 +77,7 @@ config: | |||
76 | # redirect_current_page: 'To the current page' | 77 | # redirect_current_page: 'To the current page' |
77 | pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket | 78 | pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket |
78 | # android_configuration: Configure your Android application | 79 | # android_configuration: Configure your Android application |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." | 81 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." |
80 | # help_items_per_page: "You can change the number of articles displayed on each page." | 82 | # help_items_per_page: "You can change the number of articles displayed on each page." |
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." | 83 | # 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." |
@@ -89,9 +91,10 @@ config: | |||
89 | token_reset: 'Resetează-ți token-ul' | 91 | token_reset: 'Resetează-ți token-ul' |
90 | rss_links: 'Link-uri RSS' | 92 | rss_links: 'Link-uri RSS' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'unread' | 94 | unread: 'Unread' |
93 | starred: 'starred' | 95 | starred: 'Starred' |
94 | archive: 'archived' | 96 | archive: 'Archived' |
97 | # all: 'All' | ||
95 | rss_limit: 'Limită RSS' | 98 | rss_limit: 'Limită RSS' |
96 | form_user: | 99 | form_user: |
97 | # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" | 100 | # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" |
@@ -110,6 +113,7 @@ config: | |||
110 | # annotations: Remove ALL annotations | 113 | # annotations: Remove ALL annotations |
111 | # tags: Remove ALL tags | 114 | # tags: Remove ALL tags |
112 | # entries: Remove ALL entries | 115 | # entries: Remove ALL entries |
116 | # archived: Remove ALL archived entries | ||
113 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | 117 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) |
114 | form_password: | 118 | form_password: |
115 | # description: "You can change your password here. Your new password should by at least 8 characters long." | 119 | # description: "You can change your password here. Your new password should by at least 8 characters long." |
@@ -154,6 +158,7 @@ config: | |||
154 | # or: 'One rule OR another' | 158 | # or: 'One rule OR another' |
155 | # and: 'One rule AND another' | 159 | # and: 'One rule AND another' |
156 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' | 160 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -185,6 +190,8 @@ entry: | |||
185 | unread_label: 'Necitite' | 190 | unread_label: 'Necitite' |
186 | preview_picture_label: 'Are o imagine de previzualizare' | 191 | preview_picture_label: 'Are o imagine de previzualizare' |
187 | preview_picture_help: 'Previzualizare imagine' | 192 | preview_picture_help: 'Previzualizare imagine' |
193 | # is_public_label: 'Has a public link' | ||
194 | # is_public_help: 'Public link' | ||
188 | language_label: 'Limbă' | 195 | language_label: 'Limbă' |
189 | # http_status_label: 'HTTP status' | 196 | # http_status_label: 'HTTP status' |
190 | reading_time: | 197 | reading_time: |
@@ -223,6 +230,8 @@ entry: | |||
223 | original_article: 'original' | 230 | original_article: 'original' |
224 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 231 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Data creării' | 232 | created_at: 'Data creării' |
233 | # published_at: 'Publication date' | ||
234 | # published_by: 'Published by' | ||
226 | new: | 235 | new: |
227 | page_title: 'Salvează un nou articol' | 236 | page_title: 'Salvează un nou articol' |
228 | placeholder: 'http://website.com' | 237 | placeholder: 'http://website.com' |
@@ -234,10 +243,12 @@ entry: | |||
234 | # page_title: 'Edit an entry' | 243 | # page_title: 'Edit an entry' |
235 | # title_label: 'Title' | 244 | # title_label: 'Title' |
236 | url_label: 'Url' | 245 | url_label: 'Url' |
237 | # is_public_label: 'Public' | ||
238 | save_label: 'Salvează' | 246 | save_label: 'Salvează' |
239 | public: | 247 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 248 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
249 | confirm: | ||
250 | # delete: "Are you sure you want to remove that article?" | ||
251 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 252 | ||
242 | about: | 253 | about: |
243 | page_title: 'Despre' | 254 | page_title: 'Despre' |
@@ -510,6 +521,28 @@ user: | |||
510 | # delete: Delete | 521 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 522 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 523 | # back_to_list: Back to list |
524 | search: | ||
525 | # placeholder: Filter by username or email | ||
526 | |||
527 | site_credential: | ||
528 | # page_title: Site credentials management | ||
529 | # new_site_credential: Create a credential | ||
530 | # edit_site_credential: Edit an existing credential | ||
531 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
532 | # list: | ||
533 | # actions: Actions | ||
534 | # edit_action: Edit | ||
535 | # yes: Yes | ||
536 | # no: No | ||
537 | # create_new_one: Create a new credential | ||
538 | # form: | ||
539 | # username_label: 'Username' | ||
540 | # host_label: 'Host' | ||
541 | # password_label: 'Password' | ||
542 | # save: Save | ||
543 | # delete: Delete | ||
544 | # delete_confirm: Are you sure? | ||
545 | # back_to_list: Back to list | ||
513 | 546 | ||
514 | error: | 547 | error: |
515 | # page_title: An error occurred | 548 | # page_title: An error occurred |
@@ -528,6 +561,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 561 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 562 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 563 | # entries_reset: Entries reset |
564 | # archived_reset: Archived entries deleted | ||
531 | entry: | 565 | entry: |
532 | notice: | 566 | notice: |
533 | # entry_already_saved: 'Entry already saved on %date%' | 567 | # entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +596,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 596 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 597 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 598 | # deleted: 'User "%username%" deleted' |
599 | site_credential: | ||
600 | notice: | ||
601 | # added: 'Site credential for "%host%" added' | ||
602 | # updated: 'Site credential for "%host%" updated' | ||
603 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index d3180f42..90a140cd 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml | |||
@@ -32,6 +32,7 @@ menu: | |||
32 | # save_link: 'Save a link' | 32 | # save_link: 'Save a link' |
33 | back_to_unread: 'Okunmayan makalelere geri dön' | 33 | back_to_unread: 'Okunmayan makalelere geri dön' |
34 | # users_management: 'Users management' | 34 | # users_management: 'Users management' |
35 | # site_credentials: 'Site credentials' | ||
35 | top: | 36 | top: |
36 | add_new_entry: 'Yeni bir makale ekle' | 37 | add_new_entry: 'Yeni bir makale ekle' |
37 | search: 'Ara' | 38 | search: 'Ara' |
@@ -76,6 +77,7 @@ config: | |||
76 | # redirect_current_page: 'To the current page' | 77 | # redirect_current_page: 'To the current page' |
77 | # pocket_consumer_key_label: Consumer key for Pocket to import contents | 78 | # pocket_consumer_key_label: Consumer key for Pocket to import contents |
78 | # android_configuration: Configure your Android application | 79 | # android_configuration: Configure your Android application |
80 | # android_instruction: "Touch here to prefill your Android application" | ||
79 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." | 81 | # help_theme: "wallabag is customizable. You can choose your prefered theme here." |
80 | # help_items_per_page: "You can change the number of articles displayed on each page." | 82 | # help_items_per_page: "You can change the number of articles displayed on each page." |
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." | 83 | # 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." |
@@ -89,9 +91,10 @@ config: | |||
89 | token_reset: 'Belirteci (token) sıfırla' | 91 | token_reset: 'Belirteci (token) sıfırla' |
90 | rss_links: 'RSS akış bağlantıları' | 92 | rss_links: 'RSS akış bağlantıları' |
91 | rss_link: | 93 | rss_link: |
92 | unread: 'okunmayan' | 94 | unread: 'Okunmayan' |
93 | starred: 'favoriler' | 95 | starred: 'Favoriler' |
94 | archive: 'arşiv' | 96 | archive: 'Arşiv' |
97 | # all: 'All' | ||
95 | rss_limit: 'RSS içeriğinden talep edilecek makale limiti' | 98 | rss_limit: 'RSS içeriğinden talep edilecek makale limiti' |
96 | form_user: | 99 | form_user: |
97 | two_factor_description: "İki adımlı doğrulamayı aktifleştirdiğinizde, her yeni güvenilmeyen bağlantılarda size e-posta ile bir kod alacaksınız." | 100 | two_factor_description: "İki adımlı doğrulamayı aktifleştirdiğinizde, her yeni güvenilmeyen bağlantılarda size e-posta ile bir kod alacaksınız." |
@@ -110,6 +113,7 @@ config: | |||
110 | # annotations: Remove ALL annotations | 113 | # annotations: Remove ALL annotations |
111 | # tags: Remove ALL tags | 114 | # tags: Remove ALL tags |
112 | # entries: Remove ALL entries | 115 | # entries: Remove ALL entries |
116 | # archived: Remove ALL archived entries | ||
113 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | 117 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) |
114 | form_password: | 118 | form_password: |
115 | # description: "You can change your password here. Your new password should by at least 8 characters long." | 119 | # description: "You can change your password here. Your new password should by at least 8 characters long." |
@@ -154,6 +158,7 @@ config: | |||
154 | or: 'Bir kural veya birbaşkası' | 158 | or: 'Bir kural veya birbaşkası' |
155 | and: 'Bir kural ve diğeri' | 159 | and: 'Bir kural ve diğeri' |
156 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' | 160 | # matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>' |
161 | # notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>' | ||
157 | 162 | ||
158 | entry: | 163 | entry: |
159 | page_titles: | 164 | page_titles: |
@@ -223,6 +228,8 @@ entry: | |||
223 | original_article: 'orijinal' | 228 | original_article: 'orijinal' |
224 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 229 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Oluşturulma tarihi' | 230 | created_at: 'Oluşturulma tarihi' |
231 | # published_at: 'Publication date' | ||
232 | # published_by: 'Published by' | ||
226 | new: | 233 | new: |
227 | page_title: 'Yeni makaleyi kaydet' | 234 | page_title: 'Yeni makaleyi kaydet' |
228 | placeholder: 'http://website.com' | 235 | placeholder: 'http://website.com' |
@@ -234,10 +241,12 @@ entry: | |||
234 | page_title: 'Makaleyi düzenle' | 241 | page_title: 'Makaleyi düzenle' |
235 | title_label: 'Başlık' | 242 | title_label: 'Başlık' |
236 | url_label: 'Url' | 243 | url_label: 'Url' |
237 | is_public_label: 'Herkes tarafından erişime açık olsun mu?' | ||
238 | save_label: 'Kaydet' | 244 | save_label: 'Kaydet' |
239 | public: | 245 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 246 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
247 | confirm: | ||
248 | # delete: "Are you sure you want to remove that article?" | ||
249 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 250 | ||
242 | about: | 251 | about: |
243 | page_title: 'Hakkımızda' | 252 | page_title: 'Hakkımızda' |
@@ -510,6 +519,8 @@ user: | |||
510 | # delete: Delete | 519 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 520 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 521 | # back_to_list: Back to list |
522 | search: | ||
523 | # placeholder: Filter by username or email | ||
513 | 524 | ||
514 | error: | 525 | error: |
515 | # page_title: An error occurred | 526 | # page_title: An error occurred |
@@ -528,6 +539,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 539 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 540 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 541 | # entries_reset: Entries reset |
542 | # archived_reset: Archived entries deleted | ||
531 | entry: | 543 | entry: |
532 | notice: | 544 | notice: |
533 | entry_already_saved: 'Entry already saved on %date%' | 545 | entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +574,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 574 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 575 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 576 | # deleted: 'User "%username%" deleted' |
577 | site_credential: | ||
578 | notice: | ||
579 | # added: 'Site credential for "%host%" added' | ||
580 | # updated: 'Site credential for "%host%" updated' | ||
581 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml index 32a8b4a8..c6a84209 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | # password_wrong_value: 'Wrong value for your current password' | 4 | # password_wrong_value: 'Wrong value for your current password' |
5 | # item_per_page_too_high: 'This will certainly kill the app' | 5 | # item_per_page_too_high: 'This will certainly kill the app' |
6 | # rss_limit_too_high: 'This will certainly kill the app' | 6 | # rss_limit_too_high: 'This will certainly kill the app' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml index 37b9888f..c74c00ca 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'Falscher Wert für dein aktuelles Kennwort' | 4 | password_wrong_value: 'Falscher Wert für dein aktuelles Kennwort' |
5 | item_per_page_too_high: 'Dies wird die Anwendung möglicherweise beenden' | 5 | item_per_page_too_high: 'Dies wird die Anwendung möglicherweise beenden' |
6 | rss_limit_too_high: 'Dies wird die Anwendung möglicherweise beenden' | 6 | rss_limit_too_high: 'Dies wird die Anwendung möglicherweise beenden' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml index 29217497..8cc117fe 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'Wrong value for your current password' | 4 | password_wrong_value: 'Wrong value for your current password' |
5 | item_per_page_too_high: 'This will certainly kill the app' | 5 | item_per_page_too_high: 'This will certainly kill the app' |
6 | rss_limit_too_high: 'This will certainly kill the app' | 6 | rss_limit_too_high: 'This will certainly kill the app' |
7 | quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml index 57ddaa5a..97a8edfa 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'Entrada equivocada para su contraseña actual' | 4 | password_wrong_value: 'Entrada equivocada para su contraseña actual' |
5 | item_per_page_too_high: 'Esto matará la aplicación' | 5 | item_per_page_too_high: 'Esto matará la aplicación' |
6 | rss_limit_too_high: 'Esto matará la aplicación' | 6 | rss_limit_too_high: 'Esto matará la aplicación' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml index e0536d18..ef677525 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'رمز فعلی را اشتباه وارد کردهاید' | 4 | password_wrong_value: 'رمز فعلی را اشتباه وارد کردهاید' |
5 | item_per_page_too_high: 'با این تعداد برنامه به فنا میرود' | 5 | item_per_page_too_high: 'با این تعداد برنامه به فنا میرود' |
6 | rss_limit_too_high: 'با این تعداد برنامه به فنا میرود' | 6 | rss_limit_too_high: 'با این تعداد برنامه به فنا میرود' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml index 64574709..f31b4ed2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: "Votre mot de passe actuel est faux" | 4 | password_wrong_value: "Votre mot de passe actuel est faux" |
5 | item_per_page_too_high: "Ça ne va pas plaire à l’application" | 5 | item_per_page_too_high: "Ça ne va pas plaire à l’application" |
6 | rss_limit_too_high: "Ça ne va pas plaire à l’application" | 6 | rss_limit_too_high: "Ça ne va pas plaire à l’application" |
7 | quote_length_too_high: "La citation est trop longue. Elle doit avoir au maximum {{ limit }} caractères." | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml index d9beb54f..d949cc3b 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'Valore inserito per la password corrente errato' | 4 | password_wrong_value: 'Valore inserito per la password corrente errato' |
5 | item_per_page_too_high: 'Questo valore è troppo alto' | 5 | item_per_page_too_high: 'Questo valore è troppo alto' |
6 | rss_limit_too_high: 'Questo valore è troppo alto' | 6 | rss_limit_too_high: 'Questo valore è troppo alto' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml index f92c2708..fb4aa592 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'Vòstre senhal actual es pas bon' | 4 | password_wrong_value: 'Vòstre senhal actual es pas bon' |
5 | item_per_page_too_high: "Aquò li agradarà pas a l'aplicacion" | 5 | item_per_page_too_high: "Aquò li agradarà pas a l'aplicacion" |
6 | rss_limit_too_high: "Aquò li agradarà pas a l'aplicacion" | 6 | rss_limit_too_high: "Aquò li agradarà pas a l'aplicacion" |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml index ffcd5e7f..e4165c14 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'Twoje obecne hasło jest błędne' | 4 | password_wrong_value: 'Twoje obecne hasło jest błędne' |
5 | item_per_page_too_high: 'To może spowodować problemy z aplikacją' | 5 | item_per_page_too_high: 'To może spowodować problemy z aplikacją' |
6 | rss_limit_too_high: 'To może spowodować problemy z aplikacją' | 6 | rss_limit_too_high: 'To może spowodować problemy z aplikacją' |
7 | quote_length_too_high: 'Cytat jest zbyt długi. powinien mieć {{ limit }} znaków lub mniej.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml index 4eddff10..a8c1f9de 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | password_wrong_value: 'A senha atual informada está errada' | 4 | password_wrong_value: 'A senha atual informada está errada' |
5 | item_per_page_too_high: 'Certamente isso pode matar a aplicação' | 5 | item_per_page_too_high: 'Certamente isso pode matar a aplicação' |
6 | rss_limit_too_high: 'Certamente isso pode matar a aplicação' | 6 | rss_limit_too_high: 'Certamente isso pode matar a aplicação' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml index 59a8cdd8..6840cf11 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | # password_wrong_value: 'Wrong value for your current password' | 4 | # password_wrong_value: 'Wrong value for your current password' |
5 | # item_per_page_too_high: 'This will certainly kill the app' | 5 | # item_per_page_too_high: 'This will certainly kill the app' |
6 | # rss_limit_too_high: 'This will certainly kill the app' | 6 | # rss_limit_too_high: 'This will certainly kill the app' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml index 01388771..e1e7317f 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml | |||
@@ -4,3 +4,4 @@ validator: | |||
4 | # password_wrong_value: 'Wrong value for your current password' | 4 | # password_wrong_value: 'Wrong value for your current password' |
5 | # item_per_page_too_high: 'This will certainly kill the app' | 5 | # item_per_page_too_high: 'This will certainly kill the app' |
6 | # rss_limit_too_high: 'This will certainly kill the app' | 6 | # rss_limit_too_high: 'This will certainly kill the app' |
7 | # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' | ||
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 3548f590..bcc57dac 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 | |||
@@ -82,7 +82,7 @@ | |||
82 | <fieldset class="w500p inline"> | 82 | <fieldset class="w500p inline"> |
83 | <div class="row"> | 83 | <div class="row"> |
84 | <h3>{{ 'config.form_settings.android_configuration'|trans }}</h3> | 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> | 85 | <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}">{{ 'config.form_settings.android_instruction' | trans }}</a> |
86 | <br/> | 86 | <br/> |
87 | <img id="androidQrcode" /> | 87 | <img id="androidQrcode" /> |
88 | <script> | 88 | <script> |
@@ -106,7 +106,7 @@ | |||
106 | 106 | ||
107 | <fieldset class="w500p inline"> | 107 | <fieldset class="w500p inline"> |
108 | <div class="row"> | 108 | <div class="row"> |
109 | <label>Rss token</label> | 109 | <label>{{ 'config.form_rss.token_label'|trans }}</label> |
110 | {% if rss.token %} | 110 | {% if rss.token %} |
111 | {{ rss.token }} | 111 | {{ rss.token }} |
112 | {% else %} | 112 | {% else %} |
@@ -128,9 +128,10 @@ | |||
128 | <div class="row"> | 128 | <div class="row"> |
129 | <label>{{ 'config.form_rss.rss_links'|trans }}</label> | 129 | <label>{{ 'config.form_rss.rss_links'|trans }}</label> |
130 | <ul> | 130 | <ul> |
131 | <li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">unread</a></li> | 131 | <li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.unread'|trans }}</a></li> |
132 | <li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">fav</a></li> | 132 | <li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.starred'|trans }}</a></li> |
133 | <li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">archives</a></li> | 133 | <li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.archive'|trans }}</a></li> |
134 | <li><a href="{{ path('all_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.all'|trans }}</a></li> | ||
134 | </ul> | 135 | </ul> |
135 | </div> | 136 | </div> |
136 | </fieldset> | 137 | </fieldset> |
@@ -200,6 +201,11 @@ | |||
200 | </a> | 201 | </a> |
201 | </li> | 202 | </li> |
202 | <li> | 203 | <li> |
204 | <a href="{{ path('config_reset', { type: 'archived'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
205 | {{ 'config.reset.archived'|trans }} | ||
206 | </a> | ||
207 | </li> | ||
208 | <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"> | 209 | <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 }} | 210 | {{ 'config.reset.entries'|trans }} |
205 | </a> | 211 | </a> |
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 859b166b..6424df8d 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,5 +1,12 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | 1 | {% extends "WallabagCoreBundle::layout.html.twig" %} |
2 | 2 | ||
3 | {% block head %} | ||
4 | {{ parent() }} | ||
5 | {% if tag is defined and app.user.config.rssToken %} | ||
6 | <link rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" /> | ||
7 | {% endif %} | ||
8 | {% endblock %} | ||
9 | |||
3 | {% block title %} | 10 | {% block title %} |
4 | {% set filter = '' %} | 11 | {% set filter = '' %} |
5 | {% if tag is defined %} | 12 | {% if tag is defined %} |
@@ -12,14 +19,17 @@ | |||
12 | {% endblock %} | 19 | {% endblock %} |
13 | 20 | ||
14 | {% block content %} | 21 | {% block content %} |
15 | 22 | {% set currentRoute = app.request.attributes.get('_route') %} | |
16 | {% set listMode = app.user.config.listMode %} | 23 | {% set listMode = app.user.config.listMode %} |
17 | <div class="results"> | 24 | <div class="results"> |
18 | <div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div> | 25 | <div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div> |
19 | <div class="pagination"> | 26 | <div class="pagination"> |
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> | 27 | <a href="{{ path('switch_view_mode') }}"><i class="listMode-btn material-icons md-24">{% if listMode == 0 %}list{% else %}view_module{% endif %}</i></a> |
21 | <i class="btn-clickable download-btn material-icons md-36 js-export-action">file_download</i> | 28 | {% if app.user.config.rssToken %} |
22 | <i class="btn-clickable filter-btn material-icons md-36 js-filters-action">filter_list</i> | 29 | {% include "@WallabagCore/themes/common/Entry/_rss_link.html.twig" %} |
30 | {% endif %} | ||
31 | <i class="btn-clickable download-btn material-icons md-24 js-export-action">file_download</i> | ||
32 | <i class="btn-clickable filter-btn material-icons md-24 js-filters-action">filter_list</i> | ||
23 | {% if entries.getNbPages > 1 %} | 33 | {% if entries.getNbPages > 1 %} |
24 | {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} | 34 | {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} |
25 | {% endif %} | 35 | {% endif %} |
@@ -47,10 +57,10 @@ | |||
47 | </div> | 57 | </div> |
48 | 58 | ||
49 | <ul class="tools links"> | 59 | <ul class="tools links"> |
50 | <li><a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool icon-check icon {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %}" href="{{ path('archive_entry', { 'id': entry.id }) }}"><span>{{ 'entry.list.toogle_as_read'|trans }}</span></a></li> | 60 | <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}"><span>{{ entry.domainName|removeWww }}</span></a></li> |
51 | <li><a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool icon-star icon {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><span>{{ 'entry.list.toogle_as_star'|trans }}</span></a></li> | 61 | <li><a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool icon {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %}" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">check</i><span>{{ 'entry.list.toogle_as_read'|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> | 62 | <li><a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool icon {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">star_rate</i><span>{{ 'entry.list.toogle_as_star'|trans }}</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> | 63 | <li><a title="{{ 'entry.list.delete'|trans }}" class="tool icon" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">delete</i><span>{{ 'entry.list.delete'|trans }}</span></a></li> |
54 | </ul> | 64 | </ul> |
55 | {% if (entry.previewPicture is null or listMode == 1) %} | 65 | {% if (entry.previewPicture is null or listMode == 1) %} |
56 | <ul class="card-entry-tags"> | 66 | <ul class="card-entry-tags"> |
@@ -76,7 +86,6 @@ | |||
76 | 86 | ||
77 | <!-- Export --> | 87 | <!-- Export --> |
78 | <aside id="download-form"> | 88 | <aside id="download-form"> |
79 | {% set currentRoute = app.request.attributes.get('_route') %} | ||
80 | {% set currentTag = '' %} | 89 | {% set currentTag = '' %} |
81 | {% if tag is defined %} | 90 | {% if tag is defined %} |
82 | {% set currentTag = tag %} | 91 | {% set currentTag = tag %} |
@@ -127,6 +136,11 @@ | |||
127 | {{ form_widget(form.previewPicture) }} | 136 | {{ form_widget(form.previewPicture) }} |
128 | {{ form_label(form.previewPicture) }} | 137 | {{ form_label(form.previewPicture) }} |
129 | </div> | 138 | </div> |
139 | |||
140 | <div class="input-field"> | ||
141 | {{ form_widget(form.isPublic) }} | ||
142 | {{ form_label(form.isPublic) }} | ||
143 | </div> | ||
130 | </div> | 144 | </div> |
131 | 145 | ||
132 | <div id="filter-language" class="filter-group"> | 146 | <div id="filter-language" class="filter-group"> |
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 a555691d..3d20a6bc 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 | |||
@@ -22,7 +22,7 @@ | |||
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> | 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 %} favorite" 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 }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" 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') %} |
27 | <li><a href="{{ path('share', {'id': entry.id }) }}" target="_blank" class="tool icon icon-eye" title="{{ 'entry.view.left_menu.public_link'|trans }}"><span>{{ 'entry.view.left_menu.public_link'|trans }}</span></a></li> | 27 | <li><a href="{{ path('share', {'id': entry.id }) }}" target="_blank" class="tool icon icon-eye" title="{{ 'entry.view.left_menu.public_link'|trans }}"><span>{{ 'entry.view.left_menu.public_link'|trans }}</span></a></li> |
28 | <li><a href="{{ path('delete_share', {'id': entry.id }) }}" class="tool icon icon-no-eye" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}"><span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span></a></li> | 28 | <li><a href="{{ path('delete_share', {'id': entry.id }) }}" class="tool icon icon-no-eye" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}"><span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span></a></li> |
@@ -30,6 +30,7 @@ | |||
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 }}&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 }}&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 }}&title={{ entry.title|url_encode }}&tags={{ entry.tags|join(',')|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 }}&title={{ entry.title|url_encode }}&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_scuttle') %}<li><a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&address={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&tags={{ entry.tags|join(',')|url_encode }}" target="_blank" class="tool icon-image icon-image--scuttle" title="scuttle"><span>scuttle</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 }}¬es=&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_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}¬es=&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 }}&title={{entry.title|url_encode}}&v=6" target="_blank" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %} | 35 | {% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&title={{entry.title|url_encode}}&v=6" target="_blank" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</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 %} | 36 | {% 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 %} |
@@ -43,9 +44,23 @@ | |||
43 | 44 | ||
44 | <div id="article-informations"> | 45 | <div id="article-informations"> |
45 | <i class="tool icon icon-calendar" title="{{ 'entry.view.created_at'|trans }}"> | 46 | <i class="tool icon icon-calendar" title="{{ 'entry.view.created_at'|trans }}"> |
46 | {{ entry.createdAt|date('Y-m-d') }} | 47 | {{ entry.createdAt|date('Y-m-d H:i') }} |
47 | </i> | 48 | </i> |
48 | 49 | ||
50 | {% if entry.publishedAt is not null %} | ||
51 | <i class="tool icon icon-pencil2" title="{{ 'entry.view.published_at'|trans }}"> | ||
52 | {{ entry.publishedAt|date('Y-m-d H:i') }} | ||
53 | </i> | ||
54 | {% endif %} | ||
55 | |||
56 | {% if entry.publishedBy is not empty %} | ||
57 | <i class="tool icon icon-users" title="{{ 'entry.view.published_by'|trans }}"> | ||
58 | {% for author in entry.publishedBy %} | ||
59 | {{ author }}{% if not loop.last %}, {% endif %} | ||
60 | {% endfor %} | ||
61 | </i> | ||
62 | {% endif %} | ||
63 | |||
49 | <i class="tool icon icon-time"> | 64 | <i class="tool icon icon-time"> |
50 | {% set readingTime = entry.readingTime / app.user.config.readingSpeed %} | 65 | {% set readingTime = entry.readingTime / app.user.config.readingSpeed %} |
51 | {% if readingTime > 0 %} | 66 | {% if readingTime > 0 %} |
@@ -59,10 +74,16 @@ | |||
59 | <aside class="tags"> | 74 | <aside class="tags"> |
60 | <div class="card-entry-tags"> | 75 | <div class="card-entry-tags"> |
61 | {% for tag in entry.tags %} | 76 | {% for tag in entry.tags %} |
62 | <span class="label-outline"><i class="material-icons">label_outline</i> <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}" class="nostyle"><i>✘</i></a></span> | 77 | <span class="label-outline"> |
78 | <i class="material-icons">label_outline</i> | ||
79 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> | ||
80 | <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}" onclick="return confirm('{{ 'entry.confirm.delete_tag'|trans|escape('js') }}')" class="nostyle"> | ||
81 | <i>✘</i> | ||
82 | </a> | ||
83 | </span> | ||
63 | {% endfor %} | 84 | {% endfor %} |
64 | </div> | 85 | </div> |
65 | <div class="input-field nav-panel-add-tag" style="display: none"> | 86 | <div class="input-field baggy-add-tag" style="display: none"> |
66 | {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} | 87 | {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} |
67 | </div> | 88 | </div> |
68 | </aside> | 89 | </aside> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/edit.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/edit.html.twig new file mode 100644 index 00000000..882be430 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/edit.html.twig | |||
@@ -0,0 +1,60 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'site_credential.page_title'|trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | |||
7 | <div class="row"> | ||
8 | <div class="col s12"> | ||
9 | <div class="card-panel"> | ||
10 | <div class="row"> | ||
11 | <div class="input-field col s12"> | ||
12 | <h4>{{ 'site_credential.edit_site_credential'|trans }}</h4> | ||
13 | |||
14 | <div id="set6" class="col s12"> | ||
15 | {{ form_start(edit_form) }} | ||
16 | {{ form_errors(edit_form) }} | ||
17 | |||
18 | <div class="row"> | ||
19 | <div class="input-field col s12"> | ||
20 | {{ form_label(edit_form.host) }} | ||
21 | {{ form_errors(edit_form.host) }} | ||
22 | {{ form_widget(edit_form.host) }} | ||
23 | </div> | ||
24 | </div> | ||
25 | |||
26 | <div class="row"> | ||
27 | <div class="input-field col s12"> | ||
28 | {{ form_label(edit_form.username) }} | ||
29 | {{ form_errors(edit_form.username) }} | ||
30 | {{ form_widget(edit_form.username) }} | ||
31 | </div> | ||
32 | </div> | ||
33 | |||
34 | <div class="row"> | ||
35 | <div class="input-field col s12"> | ||
36 | {{ form_label(edit_form.password) }} | ||
37 | {{ form_errors(edit_form.password) }} | ||
38 | {{ form_widget(edit_form.password) }} | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | <br/> | ||
43 | |||
44 | {{ form_widget(edit_form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} | ||
45 | {{ form_widget(edit_form._token) }} | ||
46 | </form> | ||
47 | <p> | ||
48 | {{ form_start(delete_form) }} | ||
49 | <button onclick="return confirm('{{ 'site_credential.form.delete_confirm'|trans|escape('js') }}')" type="submit" class="btn waves-effect waves-light red">{{ 'site_credential.form.delete'|trans }}</button> | ||
50 | {{ form_end(delete_form) }} | ||
51 | </p> | ||
52 | <p><a class="waves-effect waves-light btn blue-grey" href="{{ path('site_credentials_index') }}">{{ 'site_credential.form.back_to_list'|trans }}</a></p> | ||
53 | </div> | ||
54 | </div> | ||
55 | </div> | ||
56 | </div> | ||
57 | </div> | ||
58 | </div> | ||
59 | |||
60 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/index.html.twig new file mode 100644 index 00000000..324854ad --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/index.html.twig | |||
@@ -0,0 +1,42 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'site_credential.page_title'|trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | |||
7 | <div class="row"> | ||
8 | <div class="col s12"> | ||
9 | <div class="card-panel"> | ||
10 | <div class="row"> | ||
11 | <div class="input-field col s12"> | ||
12 | <p class="help">{{ 'site_credential.description'|trans|raw }}</p> | ||
13 | |||
14 | <table class="bordered"> | ||
15 | <thead> | ||
16 | <tr> | ||
17 | <th>{{ 'site_credential.form.host_label'|trans }}</th> | ||
18 | <th>{{ 'site_credential.list.actions'|trans }}</th> | ||
19 | </tr> | ||
20 | </thead> | ||
21 | <tbody> | ||
22 | {% for credential in credentials %} | ||
23 | <tr> | ||
24 | <td>{{ credential.host }}</td> | ||
25 | <td> | ||
26 | <a href="{{ path('site_credentials_edit', { 'id': credential.id }) }}">{{ 'site_credential.list.edit_action'|trans }}</a> | ||
27 | </td> | ||
28 | </tr> | ||
29 | {% endfor %} | ||
30 | </tbody> | ||
31 | </table> | ||
32 | <br /> | ||
33 | <p> | ||
34 | <a href="{{ path('site_credentials_new') }}" class="waves-effect waves-light btn">{{ 'site_credential.list.create_new_one'|trans }}</a> | ||
35 | </p> | ||
36 | </div> | ||
37 | </div> | ||
38 | </div> | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/new.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/new.html.twig new file mode 100644 index 00000000..3c008cde --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/SiteCredential/new.html.twig | |||
@@ -0,0 +1,53 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'site_credential.page_title'|trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | |||
7 | <div class="row"> | ||
8 | <div class="col s12"> | ||
9 | <div class="card-panel"> | ||
10 | <div class="row"> | ||
11 | <div class="input-field col s12"> | ||
12 | <h4>{{ 'site_credential.new_site_credential'|trans }}</h4> | ||
13 | |||
14 | <div id="set6" class="col s12"> | ||
15 | {{ form_start(form) }} | ||
16 | {{ form_errors(form) }} | ||
17 | |||
18 | <div class="row"> | ||
19 | <div class="input-field col s12"> | ||
20 | {{ form_label(form.host) }} | ||
21 | {{ form_errors(form.host) }} | ||
22 | {{ form_widget(form.host) }} | ||
23 | </div> | ||
24 | </div> | ||
25 | |||
26 | <div class="row"> | ||
27 | <div class="input-field col s12"> | ||
28 | {{ form_label(form.username) }} | ||
29 | {{ form_errors(form.username) }} | ||
30 | {{ form_widget(form.username) }} | ||
31 | </div> | ||
32 | </div> | ||
33 | |||
34 | <div class="row"> | ||
35 | <div class="input-field col s12"> | ||
36 | {{ form_label(form.password) }} | ||
37 | {{ form_errors(form.password) }} | ||
38 | {{ form_widget(form.password) }} | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | {{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} | ||
43 | {{ form_rest(form) }} | ||
44 | </form> | ||
45 | <p><a class="waves-effect waves-light btn blue-grey" href="{{ path('site_credentials_index') }}">{{ 'site_credential.form.back_to_list'|trans }}</a></p> | ||
46 | </div> | ||
47 | </div> | ||
48 | </div> | ||
49 | </div> | ||
50 | </div> | ||
51 | </div> | ||
52 | |||
53 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Tag/tags.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Tag/tags.html.twig index 1e2c6b42..070d5629 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Tag/tags.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Tag/tags.html.twig | |||
@@ -9,7 +9,12 @@ | |||
9 | 9 | ||
10 | <ul> | 10 | <ul> |
11 | {% for tag in tags %} | 11 | {% for tag in tags %} |
12 | <li id="tag-{{ tag.id|e }}"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries | length }})</a></li> | 12 | <li id="tag-{{ tag.id|e }}"> |
13 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a> | ||
14 | <a rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" class="right"> | ||
15 | <i class="material-icons md-24">rss_feed</i> | ||
16 | </a> | ||
17 | </li> | ||
13 | {% endfor %} | 18 | {% endfor %} |
14 | </ul> | 19 | </ul> |
15 | 20 | ||
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 07ff8e14..17fa13bb 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig | |||
@@ -2,12 +2,14 @@ | |||
2 | 2 | ||
3 | {% block css %} | 3 | {% block css %} |
4 | {{ parent() }} | 4 | {{ parent() }} |
5 | <link rel="stylesheet" href="{{ asset('bundles/wallabagcore/themes/baggy/css/style.min.css') }}" media="screen,projection,print"/> | 5 | {% if not app.debug %} |
6 | <link rel="stylesheet" href="{{ asset('bundles/wallabagcore/baggy.css') }}"> | ||
7 | {% endif %} | ||
6 | {% endblock %} | 8 | {% endblock %} |
7 | 9 | ||
8 | {% block scripts %} | 10 | {% block scripts %} |
9 | {{ parent() }} | 11 | {{ parent() }} |
10 | <script src="{{ asset('bundles/wallabagcore/themes/baggy/js/baggy.min.js') }}"></script> | 12 | <script src="{{ asset('bundles/wallabagcore/baggy' ~ (app.debug ? '.dev' : '') ~ '.js') }}"></script> |
11 | {% endblock %} | 13 | {% endblock %} |
12 | 14 | ||
13 | {% block header %} | 15 | {% block header %} |
@@ -36,6 +38,9 @@ | |||
36 | {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }} | 38 | {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }} |
37 | </div> | 39 | </div> |
38 | </li> | 40 | </li> |
41 | {% if craue_setting('restricted_access') %} | ||
42 | <li class="menu site_credentials"><a href="{{ path('site_credentials_index') }}">{{ 'menu.left.site_credentials'|trans }}</a></li> | ||
43 | {% endif %} | ||
39 | <li class="menu config"><a href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a></li> | 44 | <li class="menu config"><a href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a></li> |
40 | {% if is_granted('ROLE_SUPER_ADMIN') %} | 45 | {% if is_granted('ROLE_SUPER_ADMIN') %} |
41 | <li class="menu users"><a href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a></li> | 46 | <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/Developer/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig index b3f0affb..528b055c 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig | |||
@@ -33,7 +33,7 @@ | |||
33 | <table class="striped"> | 33 | <table class="striped"> |
34 | <tr> | 34 | <tr> |
35 | <td>{{ 'developer.existing_clients.field_id'|trans }}</td> | 35 | <td>{{ 'developer.existing_clients.field_id'|trans }}</td> |
36 | <td><strong><code>{{ client.id }}_{{ client.randomId }}</code></strong></td> | 36 | <td><strong><code>{{ client.clientId }}</code></strong></td> |
37 | </tr> | 37 | </tr> |
38 | <tr> | 38 | <tr> |
39 | <td>{{ 'developer.existing_clients.field_secret'|trans }}</td> | 39 | <td>{{ 'developer.existing_clients.field_secret'|trans }}</td> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_rss_link.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_rss_link.html.twig new file mode 100644 index 00000000..2bf9b2bd --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_rss_link.html.twig | |||
@@ -0,0 +1,6 @@ | |||
1 | {% if tag is defined %} | ||
2 | <a rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" class="right"><i class="material-icons md-24">rss_feed</i></a> | ||
3 | {% elseif currentRoute in ['unread', 'starred', 'archive', 'all'] %} | ||
4 | <a rel="alternate" type="application/rss+xml" href="{{ path(currentRoute ~ '_rss', {'username': app.user.username, 'token': app.user.config.rssToken}) }}" class="right"><i class="material-icons">rss_feed</i></a> | ||
5 | {% endif %} | ||
6 | |||
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 12e8c79f..d70aa5dc 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 | |||
@@ -1,8 +1,8 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
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(type) }}</link> | 5 | <link>{{ url_html }}</link> |
6 | <link rel="self" href="{{ app.request.uri }}"/> | 6 | <link rel="self" href="{{ app.request.uri }}"/> |
7 | {% if entries.hasPreviousPage -%} | 7 | {% if entries.hasPreviousPage -%} |
8 | <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/> | 8 | <link rel="previous" href="{{ url }}?page={{ entries.previousPage }}"/> |
@@ -13,7 +13,7 @@ | |||
13 | <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/> | 13 | <link rel="last" href="{{ url }}?page={{ entries.nbPages }}"/> |
14 | <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> | 14 | <pubDate>{{ "now"|date('D, d M Y H:i:s') }}</pubDate> |
15 | <generator>wallabag</generator> | 15 | <generator>wallabag</generator> |
16 | <description>wallabag {{type}} elements</description> | 16 | <description>wallabag {{ type }} elements</description> |
17 | 17 | ||
18 | {% for entry in entries %} | 18 | {% for entry in entries %} |
19 | 19 | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig index 623cf1c4..a67807f9 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig | |||
@@ -1,32 +1,6 @@ | |||
1 | <html> | 1 | <html> |
2 | <head> | 2 | <head> |
3 | <title>{{ entry.title|e|raw }}</title> | 3 | <title>{{ entry.title|e|raw }}</title> |
4 | <style> | ||
5 | body { | ||
6 | margin: 10px; | ||
7 | font-family: 'Roboto',Verdana,Geneva,sans-serif; | ||
8 | font-size: 16px; | ||
9 | color: #000; | ||
10 | } | ||
11 | header { | ||
12 | text-align: center; | ||
13 | } | ||
14 | |||
15 | header h1 { | ||
16 | font-size: 1.3em; | ||
17 | } | ||
18 | |||
19 | a, | ||
20 | a:hover, | ||
21 | a:visited { | ||
22 | color: #000; | ||
23 | } | ||
24 | |||
25 | article { | ||
26 | margin: 0 auto; | ||
27 | width: 600px; | ||
28 | } | ||
29 | </style> | ||
30 | <meta property="og:title" content="{{ entry.title|e|raw }}" /> | 4 | <meta property="og:title" content="{{ entry.title|e|raw }}" /> |
31 | <meta property="og:type" content="article" /> | 5 | <meta property="og:type" content="article" /> |
32 | <meta property="og:url" content="{{ app.request.uri }}" /> | 6 | <meta property="og:url" content="{{ app.request.uri }}" /> |
@@ -40,12 +14,22 @@ | |||
40 | <meta name="twitter:site" content="@wallabagapp" /> | 14 | <meta name="twitter:site" content="@wallabagapp" /> |
41 | <meta name="twitter:title" content="{{ entry.title|e|raw }}" /> | 15 | <meta name="twitter:title" content="{{ entry.title|e|raw }}" /> |
42 | <meta name="twitter:description" content="{{ entry.content|striptags|slice(0, 300)|raw }}…" /> | 16 | <meta name="twitter:description" content="{{ entry.content|striptags|slice(0, 300)|raw }}…" /> |
17 | {% if app.debug %} | ||
18 | <script src="{{ asset('bundles/wallabagcore/public.dev.js') }}"></script> | ||
19 | {% else %} | ||
20 | <link rel="stylesheet" href="{{ asset('bundles/wallabagcore/public.css') }}"> | ||
21 | {% endif %} | ||
22 | |||
43 | </head> | 23 | </head> |
44 | <body> | 24 | <body> |
45 | <header> | 25 | <header> |
46 | <h1>{{ entry.title|e|raw }}</h1> | 26 | <h1>{{ entry.title|e|raw }}</h1> |
47 | <div><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e|raw }}" class="tool">{{ entry.domainName|removeWww }}</a></div> | 27 | <div><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e|raw }}" class="tool">{{ entry.domainName|removeWww }}</a></div> |
48 | <div>{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage')})|raw }}</div> | 28 | <div>{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage'), '%username%': entry.user.username})|raw }}.</div> |
29 | |||
30 | {% if entry.previewPicture is not null %} | ||
31 | <div><img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|striptags|e('html_attr') }}" /></div> | ||
32 | {% endif %} | ||
49 | </header> | 33 | </header> |
50 | <article> | 34 | <article> |
51 | {{ entry.content | raw }} | 35 | {{ entry.content | raw }} |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 5d411fdd..a8143315 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 | |||
@@ -25,9 +25,9 @@ | |||
25 | 25 | ||
26 | <div class="row"> | 26 | <div class="row"> |
27 | <div class="input-field col s11"> | 27 | <div class="input-field col s11"> |
28 | {{ form_label(form.config.theme) }} | ||
29 | {{ form_errors(form.config.theme) }} | 28 | {{ form_errors(form.config.theme) }} |
30 | {{ form_widget(form.config.theme) }} | 29 | {{ form_widget(form.config.theme) }} |
30 | {{ form_label(form.config.theme) }} | ||
31 | </div> | 31 | </div> |
32 | <div class="input-field col s1"> | 32 | <div class="input-field col s1"> |
33 | <a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_theme'|trans }}"> | 33 | <a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_theme'|trans }}"> |
@@ -38,9 +38,9 @@ | |||
38 | 38 | ||
39 | <div class="row"> | 39 | <div class="row"> |
40 | <div class="input-field col s11"> | 40 | <div class="input-field col s11"> |
41 | {{ form_label(form.config.items_per_page) }} | ||
42 | {{ form_errors(form.config.items_per_page) }} | 41 | {{ form_errors(form.config.items_per_page) }} |
43 | {{ form_widget(form.config.items_per_page) }} | 42 | {{ form_widget(form.config.items_per_page) }} |
43 | {{ form_label(form.config.items_per_page) }} | ||
44 | </div> | 44 | </div> |
45 | <div class="input-field col s1"> | 45 | <div class="input-field col s1"> |
46 | <a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_items_per_page'|trans }}"> | 46 | <a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_items_per_page'|trans }}"> |
@@ -51,9 +51,9 @@ | |||
51 | 51 | ||
52 | <div class="row"> | 52 | <div class="row"> |
53 | <div class="input-field col s11"> | 53 | <div class="input-field col s11"> |
54 | {{ form_label(form.config.reading_speed) }} | ||
55 | {{ form_errors(form.config.reading_speed) }} | 54 | {{ form_errors(form.config.reading_speed) }} |
56 | {{ form_widget(form.config.reading_speed) }} | 55 | {{ form_widget(form.config.reading_speed) }} |
56 | {{ form_label(form.config.reading_speed) }} | ||
57 | <p> | 57 | <p> |
58 | {{ 'config.form_settings.reading_speed.help_message'|trans }} | 58 | {{ 'config.form_settings.reading_speed.help_message'|trans }} |
59 | <a href="http://www.myreadspeed.com/calculate/">myreadspeed</a> | 59 | <a href="http://www.myreadspeed.com/calculate/">myreadspeed</a> |
@@ -66,19 +66,19 @@ | |||
66 | </div> | 66 | </div> |
67 | </div> | 67 | </div> |
68 | 68 | ||
69 | <div class="row"> | 69 | <div class="row"> |
70 | <div class="input-field col s12"> | 70 | <div class="input-field col s12"> |
71 | {{ form_label(form.config.action_mark_as_read) }} | 71 | {{ form_label(form.config.action_mark_as_read) }} |
72 | {{ form_errors(form.config.action_mark_as_read) }} | 72 | {{ form_errors(form.config.action_mark_as_read) }} |
73 | {{ form_widget(form.config.action_mark_as_read) }} | 73 | {{ form_widget(form.config.action_mark_as_read) }} |
74 | </div> | ||
74 | </div> | 75 | </div> |
75 | </div> | ||
76 | 76 | ||
77 | <div class="row"> | 77 | <div class="row"> |
78 | <div class="input-field col s11"> | 78 | <div class="input-field col s11"> |
79 | {{ form_label(form.config.language) }} | ||
80 | {{ form_errors(form.config.language) }} | 79 | {{ form_errors(form.config.language) }} |
81 | {{ form_widget(form.config.language) }} | 80 | {{ form_widget(form.config.language) }} |
81 | {{ form_label(form.config.language) }} | ||
82 | </div> | 82 | </div> |
83 | <div class="input-field col s1"> | 83 | <div class="input-field col s1"> |
84 | <a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_language'|trans }}"> | 84 | <a href="#" class="tooltipped" data-position="left" data-delay="50" data-tooltip="{{ 'config.form_settings.help_language'|trans }}"> |
@@ -89,9 +89,9 @@ | |||
89 | 89 | ||
90 | <div class="row"> | 90 | <div class="row"> |
91 | <div class="input-field col s11"> | 91 | <div class="input-field col s11"> |
92 | {{ form_label(form.config.pocket_consumer_key) }} | ||
93 | {{ form_errors(form.config.pocket_consumer_key) }} | 92 | {{ form_errors(form.config.pocket_consumer_key) }} |
94 | {{ form_widget(form.config.pocket_consumer_key) }} | 93 | {{ form_widget(form.config.pocket_consumer_key) }} |
94 | {{ form_label(form.config.pocket_consumer_key) }} | ||
95 | <p> | 95 | <p> |
96 | » | 96 | » |
97 | <a href="https://getpocket.com/developer/docs/authentication">https://getpocket.com/developer/docs/authentication</a> | 97 | <a href="https://getpocket.com/developer/docs/authentication">https://getpocket.com/developer/docs/authentication</a> |
@@ -107,7 +107,7 @@ | |||
107 | <div class="row"> | 107 | <div class="row"> |
108 | <div class="input-field col s12"> | 108 | <div class="input-field col s12"> |
109 | <h5>{{ 'config.form_settings.android_configuration'|trans }}</h5> | 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> | 110 | <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" class="waves-effect waves-light btn hide-on-large-only">{{ 'config.form_settings.android_instruction' | trans }}</a> |
111 | <img id="androidQrcode" class="hide-on-med-and-down" /> | 111 | <img id="androidQrcode" class="hide-on-med-and-down" /> |
112 | </div> | 112 | </div> |
113 | <script> | 113 | <script> |
@@ -132,8 +132,8 @@ | |||
132 | </div> | 132 | </div> |
133 | 133 | ||
134 | <div class="row"> | 134 | <div class="row"> |
135 | <div class="input-field col s12"> | 135 | <div class="col s12"> |
136 | <label>{{ 'config.form_rss.token_label'|trans }}</label> | 136 | <h6 class="grey-text">{{ 'config.form_rss.token_label'|trans }}</h6> |
137 | <div> | 137 | <div> |
138 | {% if rss.token %} | 138 | {% if rss.token %} |
139 | {{ rss.token }} | 139 | {{ rss.token }} |
@@ -151,12 +151,13 @@ | |||
151 | </div> | 151 | </div> |
152 | {% if rss.token %} | 152 | {% if rss.token %} |
153 | <div class="row"> | 153 | <div class="row"> |
154 | <div class="input-field col s12"> | 154 | <div class="col s12"> |
155 | <label>{{ 'config.form_rss.rss_links'|trans }}</label> | 155 | <h6 class="grey-text">{{ 'config.form_rss.rss_links'|trans }}</h6> |
156 | <ul> | 156 | <ul> |
157 | <li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.unread'|trans }}</a></li> | 157 | <li><a href="{{ path('unread_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.unread'|trans }}</a></li> |
158 | <li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.starred'|trans }}</a></li> | 158 | <li><a href="{{ path('starred_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.starred'|trans }}</a></li> |
159 | <li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.archive'|trans }}</a></li> | 159 | <li><a href="{{ path('archive_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.archive'|trans }}</a></li> |
160 | <li><a href="{{ path('all_rss', {'username': rss.username, 'token': rss.token}) }}">{{ 'config.form_rss.rss_link.all'|trans }}</a></li> | ||
160 | </ul> | 161 | </ul> |
161 | </div> | 162 | </div> |
162 | </div> | 163 | </div> |
@@ -229,6 +230,9 @@ | |||
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 | <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 | {{ 'config.reset.tags'|trans }} |
231 | </a> | 232 | </a> |
233 | <a href="{{ path('config_reset', { type: 'archived'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
234 | {{ 'config.reset.archived'|trans }} | ||
235 | </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"> | 236 | <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 }} | 237 | {{ 'config.reset.entries'|trans }} |
234 | </a> | 238 | </a> |
@@ -251,11 +255,11 @@ | |||
251 | {{ form_start(form.pwd) }} | 255 | {{ form_start(form.pwd) }} |
252 | {{ form_errors(form.pwd) }} | 256 | {{ form_errors(form.pwd) }} |
253 | 257 | ||
254 | <div class="row"> | 258 | <div class="row"> |
255 | <div class="input-field col s12"> | 259 | <div class="input-field col s12"> |
256 | {{ 'config.form_password.description'|trans }} | 260 | {{ 'config.form_password.description'|trans }} |
261 | </div> | ||
257 | </div> | 262 | </div> |
258 | </div> | ||
259 | 263 | ||
260 | <div class="row"> | 264 | <div class="row"> |
261 | <div class="input-field col s12"> | 265 | <div class="input-field col s12"> |
@@ -410,8 +414,8 @@ | |||
410 | <tr> | 414 | <tr> |
411 | <td>domainName</td> | 415 | <td>domainName</td> |
412 | <td>{{ 'config.form_rules.faq.variable_description.domainName'|trans }}</td> | 416 | <td>{{ 'config.form_rules.faq.variable_description.domainName'|trans }}</td> |
413 | <td>matches</td> | 417 | <td>matches<br />notmaches</td> |
414 | <td>{{ 'config.form_rules.faq.operator_description.matches'|trans|raw }}</td> | 418 | <td>{{ 'config.form_rules.faq.operator_description.matches'|trans|raw }}<br />{{ 'config.form_rules.faq.operator_description.notmatches'|trans|raw }}</td> |
415 | </tr> | 419 | </tr> |
416 | </tbody> | 420 | </tbody> |
417 | </table> | 421 | </table> |
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 d278da1b..468338ac 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 | |||
@@ -9,7 +9,7 @@ | |||
9 | <li> | 9 | <li> |
10 | <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> | 10 | <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> |
11 | <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> | 11 | <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> |
12 | <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> | 12 | <a title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" class="tool grey-text delete" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons">delete</i></a> |
13 | </li> | 13 | </li> |
14 | </ul> | 14 | </ul> |
15 | </div> | 15 | </div> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig index 3ba6253a..b64e1436 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig | |||
@@ -1,18 +1,28 @@ | |||
1 | <div class="card"> | 1 | <div class="card-stacked"> |
2 | <div class="card-stacked"> | 2 | <div class="preview">{% if entry.previewPicture is not null %}<img src="{{ entry.previewPicture }}" />{% endif %}</div> |
3 | <div class="card-content"> | 3 | <div class="card-content"> |
4 | <span class="card-title dot-ellipsis dot-resize-update"> | 4 | <span class="card-title dot-ellipsis dot-resize-update"> |
5 | <a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title | striptags | e('html_attr') }}"> | 5 | <a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title | striptags | e('html_attr') }}"> |
6 | {{ entry.title| striptags | truncate(120, true, '…') | raw }} | 6 | {{ entry.title| striptags | truncate(120, true, '…') | raw }} |
7 | </a> | 7 | </a> |
8 | </span> | 8 | </span> |
9 | <ul class="tools-list right"> | 9 | |
10 | <li> | 10 | <div class="metadata"> |
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> | 11 | <a href="{{ entry.url|e }}" class="grey-text domain" target="_blank" title="{{ entry.domainName|removeWww }}"> |
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> | 12 | <span>{{ entry.domainName|removeWww }}</span> |
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> | 13 | </a> |
14 | </li> | 14 | {% for tag in entry.tags | slice(0, 3) %} |
15 | </ul> | 15 | <span class="chip hide-on-med-and-down"> |
16 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> | ||
17 | </span> | ||
18 | {% endfor %} | ||
16 | </div> | 19 | </div> |
17 | </div> | 20 | </div> |
21 | <ul class="tools-list hide-on-small-only"> | ||
22 | <li> | ||
23 | <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> | ||
24 | <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> | ||
25 | <a title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" class="tool grey-text delete" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons">delete</i></a> | ||
26 | </li> | ||
27 | </ul> | ||
18 | </div> | 28 | </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 index 1a932a9f..6ba18768 100644 --- 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 | |||
@@ -1,7 +1,7 @@ | |||
1 | {% set readingTime = entry.readingTime / app.user.config.readingSpeed %} | 1 | {% set readingTime = entry.readingTime / app.user.config.readingSpeed %} |
2 | <i class="material-icons">timer</i> | 2 | <i class="material-icons">timer</i> |
3 | {% if readingTime > 0 %} | 3 | {% if readingTime > 0 %} |
4 | {{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }} | 4 | <span>{{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }}</span> |
5 | {% else %} | 5 | {% else %} |
6 | {{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }} | 6 | <span>{{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }}</span> |
7 | {% endif %} | 7 | {% endif %} |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/edit.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/edit.html.twig index 1c5e2aab..b9537975 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/edit.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/edit.html.twig | |||
@@ -27,11 +27,6 @@ | |||
27 | {{ form_label(form.url) }} | 27 | {{ form_label(form.url) }} |
28 | {{ form_widget(form.url) }} | 28 | {{ form_widget(form.url) }} |
29 | </div> | 29 | </div> |
30 | |||
31 | <div class="input-field s12"> | ||
32 | {{ form_widget(form.is_public) }} | ||
33 | {{ form_label(form.is_public) }} | ||
34 | </div> | ||
35 | <br> | 30 | <br> |
36 | 31 | ||
37 | {{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} | 32 | {{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} |
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 5fca53ae..0c4dc80b 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,9 +1,16 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | 1 | {% extends "WallabagCoreBundle::layout.html.twig" %} |
2 | 2 | ||
3 | {% block head %} | ||
4 | {{ parent() }} | ||
5 | {% if tag is defined and app.user.config.rssToken %} | ||
6 | <link rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" /> | ||
7 | {% endif %} | ||
8 | {% endblock %} | ||
9 | |||
3 | {% block title %} | 10 | {% block title %} |
4 | {% set filter = '' %} | 11 | {% set filter = '' %} |
5 | {% if tag is defined %} | 12 | {% if tag is defined %} |
6 | {% set filter = tag %} | 13 | {% set filter = tag.slug %} |
7 | {% endif %} | 14 | {% endif %} |
8 | {% if searchTerm is defined and searchTerm is not empty %} | 15 | {% if searchTerm is defined and searchTerm is not empty %} |
9 | {% set filter = searchTerm %} | 16 | {% set filter = searchTerm %} |
@@ -13,10 +20,14 @@ | |||
13 | 20 | ||
14 | {% block content %} | 21 | {% block content %} |
15 | {% set listMode = app.user.config.listMode %} | 22 | {% set listMode = app.user.config.listMode %} |
23 | {% set currentRoute = app.request.attributes.get('_route') %} | ||
16 | <div class="results clearfix"> | 24 | <div class="results clearfix"> |
17 | <div class="nb-results left"> | 25 | <div class="nb-results left"> |
18 | {{ 'entry.list.number_on_the_page'|transchoice(entries.count) }} | 26 | {{ '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> | 27 | <a href="{{ path('switch_view_mode') }}"><i class="material-icons">{% if listMode == 0 %}view_list{% else %}view_module{% endif %}</i></a> |
28 | {% if app.user.config.rssToken %} | ||
29 | {% include "@WallabagCore/themes/common/Entry/_rss_link.html.twig" %} | ||
30 | {% endif %} | ||
20 | </div> | 31 | </div> |
21 | {% if entries.getNbPages > 1 %} | 32 | {% if entries.getNbPages > 1 %} |
22 | {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} | 33 | {{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }} |
@@ -24,9 +35,9 @@ | |||
24 | </div> | 35 | </div> |
25 | 36 | ||
26 | <br /> | 37 | <br /> |
27 | <ul class="row data"> | 38 | <ul class="{% if listMode == 1 %}collection{% else %}row data{% endif %}"> |
28 | {% for entry in entries %} | 39 | {% for entry in entries %} |
29 | <li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% endif %} s12"> | 40 | <li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% else %}collection-item{% endif %} s12"> |
30 | {% if listMode == 1 %} | 41 | {% if listMode == 1 %} |
31 | {% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %} | 42 | {% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %} |
32 | {% elseif entry.previewPicture is null %} | 43 | {% elseif entry.previewPicture is null %} |
@@ -45,11 +56,10 @@ | |||
45 | {% endif %} | 56 | {% endif %} |
46 | 57 | ||
47 | <!-- Export --> | 58 | <!-- Export --> |
48 | <div id="export" class="side-nav fixed right-aligned"> | 59 | <div id="export" class="side-nav right-aligned"> |
49 | {% set currentRoute = app.request.attributes.get('_route') %} | ||
50 | {% set currentTag = '' %} | 60 | {% set currentTag = '' %} |
51 | {% if tag is defined %} | 61 | {% if tag is defined %} |
52 | {% set currentTag = tag %} | 62 | {% set currentTag = tag.slug %} |
53 | {% endif %} | 63 | {% endif %} |
54 | {% if currentRoute == 'homepage' %} | 64 | {% if currentRoute == 'homepage' %} |
55 | {% set currentRoute = 'unread' %} | 65 | {% set currentRoute = 'unread' %} |
@@ -68,7 +78,7 @@ | |||
68 | 78 | ||
69 | <!-- Filters --> | 79 | <!-- Filters --> |
70 | {% if form is not null %} | 80 | {% if form is not null %} |
71 | <div id="filters" class="side-nav fixed right-aligned"> | 81 | <div id="filters" class="side-nav right-aligned"> |
72 | <form action="{{ path('all') }}"> | 82 | <form action="{{ path('all') }}"> |
73 | 83 | ||
74 | <h4 class="center">{{ 'entry.filters.title'|trans }}</h4> | 84 | <h4 class="center">{{ 'entry.filters.title'|trans }}</h4> |
@@ -103,6 +113,15 @@ | |||
103 | </div> | 113 | </div> |
104 | 114 | ||
105 | <div class="col s12"> | 115 | <div class="col s12"> |
116 | <label>{{ 'entry.filters.is_public_help'|trans }}</label> | ||
117 | </div> | ||
118 | |||
119 | <div class="input-field col s12 with-checkbox"> | ||
120 | {{ form_widget(form.isPublic) }} | ||
121 | {{ form_label(form.isPublic) }} | ||
122 | </div> | ||
123 | |||
124 | <div class="col s12"> | ||
106 | {{ form_label(form.language) }} | 125 | {{ form_label(form.language) }} |
107 | </div> | 126 | </div> |
108 | 127 | ||
@@ -121,10 +140,12 @@ | |||
121 | <div class="col s12"> | 140 | <div class="col s12"> |
122 | {{ form_label(form.readingTime) }} | 141 | {{ form_label(form.readingTime) }} |
123 | </div> | 142 | </div> |
143 | |||
124 | <div class="input-field col s6"> | 144 | <div class="input-field col s6"> |
125 | {{ form_widget(form.readingTime.left_number, {'type': 'number'}) }} | 145 | {{ form_widget(form.readingTime.left_number, {'type': 'number'}) }} |
126 | <label for="entry_filter_readingTime_left_number">{{ 'entry.filters.reading_time.from'|trans }}</label> | 146 | <label for="entry_filter_readingTime_left_number">{{ 'entry.filters.reading_time.from'|trans }}</label> |
127 | </div> | 147 | </div> |
148 | |||
128 | <div class="input-field col s6"> | 149 | <div class="input-field col s6"> |
129 | {{ form_widget(form.readingTime.right_number, {'type': 'number'}) }} | 150 | {{ form_widget(form.readingTime.right_number, {'type': 'number'}) }} |
130 | <label for="entry_filter_readingTime_right_number">{{ 'entry.filters.reading_time.to'|trans }}</label> | 151 | <label for="entry_filter_readingTime_right_number">{{ 'entry.filters.reading_time.to'|trans }}</label> |
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 af53084f..4cff7bf2 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 | |||
@@ -12,6 +12,11 @@ | |||
12 | <div class="nav-wrapper cyan darken-1"> | 12 | <div class="nav-wrapper cyan darken-1"> |
13 | <ul> | 13 | <ul> |
14 | <li> | 14 | <li> |
15 | <a href="#" data-activates="slide-out" class="button-collapse"> | ||
16 | <i class="material-icons">menu</i> | ||
17 | </a> | ||
18 | </li> | ||
19 | <li> | ||
15 | <a class="waves-effect" href="{{ path('homepage') }}"> | 20 | <a class="waves-effect" href="{{ path('homepage') }}"> |
16 | <i class="material-icons">exit_to_app</i> | 21 | <i class="material-icons">exit_to_app</i> |
17 | </a> | 22 | </a> |
@@ -28,11 +33,6 @@ | |||
28 | <i class="material-icons small">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i> | 33 | <i class="material-icons small">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i> |
29 | </a> | 34 | </a> |
30 | </li> | 35 | </li> |
31 | <li> | ||
32 | <a href="#" data-activates="slide-out" class="button-collapse right"> | ||
33 | <i class="material-icons">menu</i> | ||
34 | </a> | ||
35 | </li> | ||
36 | </ul> | 36 | </ul> |
37 | </div> | 37 | </div> |
38 | </nav> | 38 | </nav> |
@@ -82,7 +82,7 @@ | |||
82 | <div class="collapsible-body"></div> | 82 | <div class="collapsible-body"></div> |
83 | </li> | 83 | </li> |
84 | <li class="bold border-bottom"> | 84 | <li class="bold border-bottom"> |
85 | <a class="waves-effect collapsible-header delete" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}"> | 85 | <a class="waves-effect collapsible-header delete" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" 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> |
@@ -125,39 +125,43 @@ | |||
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 }}&title={{ entry.title|striptags|url_encode }}&tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank"> | 128 | <a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank" title="shaarli" class="tool icon-image shaarli"> |
129 | <i class="tool icon-image icon-image--shaarli" title="shaarli"></i> | ||
130 | <span>shaarli</span> | 129 | <span>shaarli</span> |
131 | </a> | 130 | </a> |
132 | </li> | 131 | </li> |
133 | {% endif %} | 132 | {% endif %} |
133 | {% if craue_setting('share_scuttle') %} | ||
134 | <li> | ||
135 | <a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&address={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank" title="scuttle" class="tool icon-image scuttle"> | ||
136 | <span>scuttle</span> | ||
137 | </a> | ||
138 | </li> | ||
139 | {% endif %} | ||
134 | {% if craue_setting('share_diaspora') %} | 140 | {% if craue_setting('share_diaspora') %} |
135 | <li> | 141 | <li> |
136 | <a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank"> | 142 | <a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool icon-image diaspora" title="diaspora"> |
137 | <i class="tool icon-image icon-image--diaspora" title="diaspora"></i> | ||
138 | <span>diaspora*</span> | 143 | <span>diaspora*</span> |
139 | </a> | 144 | </a> |
140 | </li> | 145 | </li> |
141 | {% endif %} | 146 | {% endif %} |
142 | {% if craue_setting('share_unmark') %} | 147 | {% if craue_setting('share_unmark') %} |
143 | <li> | 148 | <li> |
144 | <a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&title={{entry.title|striptags|url_encode}}&v=6" target="_blank"> | 149 | <a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&title={{entry.title|striptags|url_encode}}&v=6" target="_blank" class="tool icon-image unmark" title="unmark"> |
145 | <i class="tool icon-image icon-image--unmark" title="unmark"></i> | ||
146 | <span>unmark.it</span> | 150 | <span>unmark.it</span> |
147 | </a> | 151 | </a> |
148 | </li> | 152 | </li> |
149 | {% endif %} | 153 | {% endif %} |
150 | {% if craue_setting('carrot') %} | 154 | {% if craue_setting('carrot') %} |
151 | <li> | 155 | <li> |
152 | <a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}" target="_blank" title="carrot"> | 156 | <a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}" target="_blank" title="carrot" class="tool icon-image carrot"> |
153 | <i class="tool icon-image icon-image--carrot"></i> | ||
154 | <span>Carrot</span> | 157 | <span>Carrot</span> |
155 | </a> | 158 | </a> |
156 | </li> | 159 | </li> |
157 | {% endif %} | 160 | {% endif %} |
158 | {% if craue_setting('share_mail') %} | 161 | {% if craue_setting('share_mail') %} |
159 | <li> | 162 | <li> |
160 | <a href="mailto:?subject={{ entry.title|striptags|url_encode }}&body={{ entry.url|url_encode }}%20via%20@wallabagapp" title="{{ 'entry.view.left_menu.share_email_label'|trans }}" class="tool email icon icon-mail"> | 163 | <a href="mailto:?subject={{ entry.title|striptags|url_encode }}&body={{ entry.url|url_encode }}%20via%20@wallabagapp" title="{{ 'entry.view.left_menu.share_email_label'|trans }}" class="tool icon"> |
164 | <i class="material-icons vertical-align-middle">mail</i> | ||
161 | <span>{{ 'entry.view.left_menu.share_email_label'|trans }}</span> | 165 | <span>{{ 'entry.view.left_menu.share_email_label'|trans }}</span> |
162 | </a> | 166 | </a> |
163 | </li> | 167 | </li> |
@@ -212,32 +216,51 @@ | |||
212 | <h1>{{ entry.title|striptags|raw }} <a href="{{ path('edit', { 'id': entry.id }) }}" title="{{ 'entry.view.edit_title'|trans }}">✎</a></h1> | 216 | <h1>{{ entry.title|striptags|raw }} <a href="{{ path('edit', { 'id': entry.id }) }}" title="{{ 'entry.view.edit_title'|trans }}">✎</a></h1> |
213 | </header> | 217 | </header> |
214 | <aside> | 218 | <aside> |
215 | <ul class="tools"> | 219 | <div class="tools"> |
216 | <li> | 220 | <ul class="stats"> |
217 | {% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %} | 221 | <li> |
218 | </li> | 222 | {% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %} |
219 | <li> | 223 | </li> |
220 | <i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i> | 224 | <li> |
221 | {{ entry.createdAt|date('Y-m-d') }} | 225 | <i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i> |
222 | </li> | 226 | {{ entry.createdAt|date('Y-m-d H:i') }} |
223 | <li> | 227 | </li> |
224 | <i class="material-icons link">link</i> | 228 | {% if entry.publishedAt is not null %} |
225 | <a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|striptags }}" class="tool"> | 229 | <li> |
226 | {{ entry.domainName|removeWww }} | 230 | <i class="material-icons" title="{{ 'entry.view.published_at'|trans }}">create</i> |
227 | </a> | 231 | {{ entry.publishedAt|date('Y-m-d H:i') }} |
228 | </li> | 232 | </li> |
229 | <li> | 233 | {% endif %} |
230 | <i class="material-icons link">comment</i> | 234 | {% if entry.publishedBy is not empty %} |
231 | {{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }} | 235 | <li> |
232 | </li> | 236 | <i class="material-icons" title="{{ 'entry.view.published_by'|trans }}">person</i> |
233 | <li id="list"> | 237 | {% for author in entry.publishedBy %} |
238 | {{ author }}{% if not loop.last %}, {% endif %} | ||
239 | {% endfor %} | ||
240 | </li> | ||
241 | {% endif %} | ||
242 | <li> | ||
243 | <i class="material-icons link">link</i> | ||
244 | <a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|striptags }}" class="tool"> | ||
245 | {{ entry.domainName|removeWww }} | ||
246 | </a> | ||
247 | </li> | ||
248 | <li> | ||
249 | <i class="material-icons link">comment</i> | ||
250 | {{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }} | ||
251 | </li> | ||
252 | </ul> | ||
253 | <ul class="tags"> | ||
234 | {% for tag in entry.tags %} | 254 | {% for tag in entry.tags %} |
235 | <div class="chip"> | 255 | <li class="chip"> |
236 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}"><i class="material-icons">delete</i></a> | 256 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> |
237 | </div> | 257 | <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}" onclick="return confirm('{{ 'entry.confirm.delete_tag'|trans|escape('js') }}')"> |
258 | <i class="material-icons vertical-align-middle">delete</i> | ||
259 | </a> | ||
260 | </li> | ||
238 | {% endfor %} | 261 | {% endfor %} |
239 | </li> | 262 | </ul> |
240 | </ul> | 263 | </div> |
241 | 264 | ||
242 | <div class="input-field nav-panel-add-tag" style="display: none"> | 265 | <div class="input-field nav-panel-add-tag" style="display: none"> |
243 | {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} | 266 | {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} |
@@ -259,7 +282,7 @@ | |||
259 | <ul> | 282 | <ul> |
260 | <li><a class="btn-floating" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons">done</i></a></li> | 283 | <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> | 284 | <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> | 285 | <li><a class="btn-floating" href="{{ path('delete_entry', { 'id': entry.id }) }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')"><i class="material-icons">delete</i></a></li> |
263 | </ul> | 286 | </ul> |
264 | </div> | 287 | </div> |
265 | </div> | 288 | </div> |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/edit.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/edit.html.twig new file mode 100644 index 00000000..882be430 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/edit.html.twig | |||
@@ -0,0 +1,60 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'site_credential.page_title'|trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | |||
7 | <div class="row"> | ||
8 | <div class="col s12"> | ||
9 | <div class="card-panel"> | ||
10 | <div class="row"> | ||
11 | <div class="input-field col s12"> | ||
12 | <h4>{{ 'site_credential.edit_site_credential'|trans }}</h4> | ||
13 | |||
14 | <div id="set6" class="col s12"> | ||
15 | {{ form_start(edit_form) }} | ||
16 | {{ form_errors(edit_form) }} | ||
17 | |||
18 | <div class="row"> | ||
19 | <div class="input-field col s12"> | ||
20 | {{ form_label(edit_form.host) }} | ||
21 | {{ form_errors(edit_form.host) }} | ||
22 | {{ form_widget(edit_form.host) }} | ||
23 | </div> | ||
24 | </div> | ||
25 | |||
26 | <div class="row"> | ||
27 | <div class="input-field col s12"> | ||
28 | {{ form_label(edit_form.username) }} | ||
29 | {{ form_errors(edit_form.username) }} | ||
30 | {{ form_widget(edit_form.username) }} | ||
31 | </div> | ||
32 | </div> | ||
33 | |||
34 | <div class="row"> | ||
35 | <div class="input-field col s12"> | ||
36 | {{ form_label(edit_form.password) }} | ||
37 | {{ form_errors(edit_form.password) }} | ||
38 | {{ form_widget(edit_form.password) }} | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | <br/> | ||
43 | |||
44 | {{ form_widget(edit_form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} | ||
45 | {{ form_widget(edit_form._token) }} | ||
46 | </form> | ||
47 | <p> | ||
48 | {{ form_start(delete_form) }} | ||
49 | <button onclick="return confirm('{{ 'site_credential.form.delete_confirm'|trans|escape('js') }}')" type="submit" class="btn waves-effect waves-light red">{{ 'site_credential.form.delete'|trans }}</button> | ||
50 | {{ form_end(delete_form) }} | ||
51 | </p> | ||
52 | <p><a class="waves-effect waves-light btn blue-grey" href="{{ path('site_credentials_index') }}">{{ 'site_credential.form.back_to_list'|trans }}</a></p> | ||
53 | </div> | ||
54 | </div> | ||
55 | </div> | ||
56 | </div> | ||
57 | </div> | ||
58 | </div> | ||
59 | |||
60 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/index.html.twig new file mode 100644 index 00000000..324854ad --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/index.html.twig | |||
@@ -0,0 +1,42 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'site_credential.page_title'|trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | |||
7 | <div class="row"> | ||
8 | <div class="col s12"> | ||
9 | <div class="card-panel"> | ||
10 | <div class="row"> | ||
11 | <div class="input-field col s12"> | ||
12 | <p class="help">{{ 'site_credential.description'|trans|raw }}</p> | ||
13 | |||
14 | <table class="bordered"> | ||
15 | <thead> | ||
16 | <tr> | ||
17 | <th>{{ 'site_credential.form.host_label'|trans }}</th> | ||
18 | <th>{{ 'site_credential.list.actions'|trans }}</th> | ||
19 | </tr> | ||
20 | </thead> | ||
21 | <tbody> | ||
22 | {% for credential in credentials %} | ||
23 | <tr> | ||
24 | <td>{{ credential.host }}</td> | ||
25 | <td> | ||
26 | <a href="{{ path('site_credentials_edit', { 'id': credential.id }) }}">{{ 'site_credential.list.edit_action'|trans }}</a> | ||
27 | </td> | ||
28 | </tr> | ||
29 | {% endfor %} | ||
30 | </tbody> | ||
31 | </table> | ||
32 | <br /> | ||
33 | <p> | ||
34 | <a href="{{ path('site_credentials_new') }}" class="waves-effect waves-light btn">{{ 'site_credential.list.create_new_one'|trans }}</a> | ||
35 | </p> | ||
36 | </div> | ||
37 | </div> | ||
38 | </div> | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/new.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/new.html.twig new file mode 100644 index 00000000..3c008cde --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/SiteCredential/new.html.twig | |||
@@ -0,0 +1,53 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | ||
2 | |||
3 | {% block title %}{{ 'site_credential.page_title'|trans }}{% endblock %} | ||
4 | |||
5 | {% block content %} | ||
6 | |||
7 | <div class="row"> | ||
8 | <div class="col s12"> | ||
9 | <div class="card-panel"> | ||
10 | <div class="row"> | ||
11 | <div class="input-field col s12"> | ||
12 | <h4>{{ 'site_credential.new_site_credential'|trans }}</h4> | ||
13 | |||
14 | <div id="set6" class="col s12"> | ||
15 | {{ form_start(form) }} | ||
16 | {{ form_errors(form) }} | ||
17 | |||
18 | <div class="row"> | ||
19 | <div class="input-field col s12"> | ||
20 | {{ form_label(form.host) }} | ||
21 | {{ form_errors(form.host) }} | ||
22 | {{ form_widget(form.host) }} | ||
23 | </div> | ||
24 | </div> | ||
25 | |||
26 | <div class="row"> | ||
27 | <div class="input-field col s12"> | ||
28 | {{ form_label(form.username) }} | ||
29 | {{ form_errors(form.username) }} | ||
30 | {{ form_widget(form.username) }} | ||
31 | </div> | ||
32 | </div> | ||
33 | |||
34 | <div class="row"> | ||
35 | <div class="input-field col s12"> | ||
36 | {{ form_label(form.password) }} | ||
37 | {{ form_errors(form.password) }} | ||
38 | {{ form_widget(form.password) }} | ||
39 | </div> | ||
40 | </div> | ||
41 | |||
42 | {{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} | ||
43 | {{ form_rest(form) }} | ||
44 | </form> | ||
45 | <p><a class="waves-effect waves-light btn blue-grey" href="{{ path('site_credentials_index') }}">{{ 'site_credential.form.back_to_list'|trans }}</a></p> | ||
46 | </div> | ||
47 | </div> | ||
48 | </div> | ||
49 | </div> | ||
50 | </div> | ||
51 | </div> | ||
52 | |||
53 | {% endblock %} | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig index c83543ac..97ddedc9 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig | |||
@@ -14,6 +14,9 @@ | |||
14 | {% for tag in tags %} | 14 | {% for tag in tags %} |
15 | <li title="{{tag.label}} ({{ tag.nbEntries }})" id="tag-{{ tag.id }}" class="col l2 m2 s5"> | 15 | <li title="{{tag.label}} ({{ tag.nbEntries }})" id="tag-{{ tag.id }}" class="col l2 m2 s5"> |
16 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a> | 16 | <a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.nbEntries }})</a> |
17 | {% if app.user.config.rssToken %} | ||
18 | <a rel="alternate" type="application/rss+xml" href="{{ path('tag_rss', {'username': app.user.username, 'token': app.user.config.rssToken, 'slug': tag.slug}) }}" class="right"><i class="material-icons">rss_feed</i></a> | ||
19 | {% endif %} | ||
17 | </li> | 20 | </li> |
18 | {% endfor %} | 21 | {% endfor %} |
19 | </ul> | 22 | </ul> |
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 3c169c04..60907e11 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig | |||
@@ -2,12 +2,14 @@ | |||
2 | 2 | ||
3 | {% block css %} | 3 | {% block css %} |
4 | {{ parent() }} | 4 | {{ parent() }} |
5 | <link rel="stylesheet" href="{{ asset('bundles/wallabagcore/themes/material/css/style.min.css') }}" media="screen,projection,print"/> | 5 | {% if not app.debug %} |
6 | <link rel="stylesheet" href="{{ asset('bundles/wallabagcore/material.css') }}"> | ||
7 | {% endif %} | ||
6 | {% endblock %} | 8 | {% endblock %} |
7 | 9 | ||
8 | {% block scripts %} | 10 | {% block scripts %} |
9 | {{ parent() }} | 11 | {{ parent() }} |
10 | <script src="{{ asset('bundles/wallabagcore/themes/material/js/material.min.js') }}"></script> | 12 | <script src="{{ asset('bundles/wallabagcore/material' ~ (app.debug ? '.dev' : '') ~ '.js') }}"></script> |
11 | {% endblock %} | 13 | {% endblock %} |
12 | 14 | ||
13 | {% block header %} | 15 | {% block header %} |
@@ -64,6 +66,11 @@ | |||
64 | <li class="bold {% if currentRoute == 'config' %}active{% endif %}"> | 66 | <li class="bold {% if currentRoute == 'config' %}active{% endif %}"> |
65 | <a class="waves-effect" href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a> | 67 | <a class="waves-effect" href="{{ path('config') }}">{{ 'menu.left.config'|trans }}</a> |
66 | </li> | 68 | </li> |
69 | {% if craue_setting('restricted_access') %} | ||
70 | <li class="bold {% if currentRoute starts with 'site_credentials_' %}active{% endif %}"> | ||
71 | <a class="waves-effect" href="{{ path('site_credentials_index') }}">{{ 'menu.left.site_credentials'|trans }}</a> | ||
72 | </li> | ||
73 | {% endif %} | ||
67 | {% if is_granted('ROLE_SUPER_ADMIN') %} | 74 | {% if is_granted('ROLE_SUPER_ADMIN') %} |
68 | <li class="bold {% if currentRoute starts with 'user_' %}active{% endif %}"> | 75 | <li class="bold {% if currentRoute starts with 'user_' %}active{% endif %}"> |
69 | <a class="waves-effect" href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a> | 76 | <a class="waves-effect" href="{{ path('user_index') }}">{{ 'menu.left.users_management'|trans }}</a> |
@@ -116,12 +123,12 @@ | |||
116 | </ul> | 123 | </ul> |
117 | <div class="input-field nav-panel-search" style="display: none"> | 124 | <div class="input-field nav-panel-search" style="display: none"> |
118 | {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }} | 125 | {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }} |
119 | <label for="search" class="active"><i class="material-icons search">search</i></label> | 126 | <label for="search"><i class="material-icons search">search</i></label> |
120 | <i class="material-icons close">clear</i> | 127 | <i class="material-icons close">clear</i> |
121 | </div> | 128 | </div> |
122 | <div class="input-field nav-panel-add" style="display: none"> | 129 | <div class="input-field nav-panel-add" style="display: none"> |
123 | {{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }} | 130 | {{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }} |
124 | <label for="add" class="active"><i class="material-icons add">add</i></label> | 131 | <label for="add"><i class="material-icons add">add</i></label> |
125 | <i class="material-icons close">clear</i> | 132 | <i class="material-icons close">clear</i> |
126 | </div> | 133 | </div> |
127 | </div> | 134 | </div> |
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php index 28d01715..5f1ab0af 100644 --- a/src/Wallabag/ImportBundle/Command/ImportCommand.php +++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php | |||
@@ -5,6 +5,7 @@ namespace Wallabag\ImportBundle\Command; | |||
5 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | 5 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; |
6 | use Symfony\Component\Config\Definition\Exception\Exception; | 6 | use Symfony\Component\Config\Definition\Exception\Exception; |
7 | use Symfony\Component\Console\Input\InputArgument; | 7 | use Symfony\Component\Console\Input\InputArgument; |
8 | use Symfony\Component\Console\Input\InputOption; | ||
8 | use Symfony\Component\Console\Input\InputInterface; | 9 | use Symfony\Component\Console\Input\InputInterface; |
9 | use Symfony\Component\Console\Output\OutputInterface; | 10 | use Symfony\Component\Console\Output\OutputInterface; |
10 | 11 | ||
@@ -15,10 +16,12 @@ class ImportCommand extends ContainerAwareCommand | |||
15 | $this | 16 | $this |
16 | ->setName('wallabag:import') | 17 | ->setName('wallabag:import') |
17 | ->setDescription('Import entries from a JSON export') | 18 | ->setDescription('Import entries from a JSON export') |
18 | ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate') | 19 | ->addArgument('username', InputArgument::REQUIRED, 'User to populate') |
19 | ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') | 20 | ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') |
20 | ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1') | 21 | ->addOption('importer', null, InputOption::VALUE_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) | 22 | ->addOption('markAsRead', null, InputOption::VALUE_OPTIONAL, 'Mark all entries as read', false) |
23 | ->addOption('useUserId', null, InputOption::VALUE_NONE, 'Use user id instead of username to find account') | ||
24 | ->addOption('disableContentUpdate', null, InputOption::VALUE_NONE, 'Disable fetching updated content from URL') | ||
22 | ; | 25 | ; |
23 | } | 26 | } |
24 | 27 | ||
@@ -34,10 +37,14 @@ class ImportCommand extends ContainerAwareCommand | |||
34 | // Turning off doctrine default logs queries for saving memory | 37 | // Turning off doctrine default logs queries for saving memory |
35 | $em->getConnection()->getConfiguration()->setSQLLogger(null); | 38 | $em->getConnection()->getConfiguration()->setSQLLogger(null); |
36 | 39 | ||
37 | $user = $em->getRepository('WallabagUserBundle:User')->findOneById($input->getArgument('userId')); | 40 | if ($input->getOption('useUserId')) { |
41 | $user = $em->getRepository('WallabagUserBundle:User')->findOneById($input->getArgument('username')); | ||
42 | } else { | ||
43 | $user = $em->getRepository('WallabagUserBundle:User')->findOneByUsername($input->getArgument('username')); | ||
44 | } | ||
38 | 45 | ||
39 | if (!is_object($user)) { | 46 | if (!is_object($user)) { |
40 | throw new Exception(sprintf('User with id "%s" not found', $input->getArgument('userId'))); | 47 | throw new Exception(sprintf('User "%s" not found', $input->getArgument('username'))); |
41 | } | 48 | } |
42 | 49 | ||
43 | switch ($input->getOption('importer')) { | 50 | switch ($input->getOption('importer')) { |
@@ -64,6 +71,7 @@ class ImportCommand extends ContainerAwareCommand | |||
64 | } | 71 | } |
65 | 72 | ||
66 | $import->setMarkAsRead($input->getOption('markAsRead')); | 73 | $import->setMarkAsRead($input->getOption('markAsRead')); |
74 | $import->setDisableContentUpdate($input->getOption('disableContentUpdate')); | ||
67 | $import->setUser($user); | 75 | $import->setUser($user); |
68 | 76 | ||
69 | $res = $import | 77 | $res = $import |
diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php index 1d4a6e27..9b624296 100644 --- a/src/Wallabag/ImportBundle/Import/AbstractImport.php +++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php | |||
@@ -8,6 +8,7 @@ use Doctrine\ORM\EntityManager; | |||
8 | use Wallabag\CoreBundle\Helper\ContentProxy; | 8 | use Wallabag\CoreBundle\Helper\ContentProxy; |
9 | use Wallabag\CoreBundle\Entity\Entry; | 9 | use Wallabag\CoreBundle\Entity\Entry; |
10 | use Wallabag\CoreBundle\Entity\Tag; | 10 | use Wallabag\CoreBundle\Entity\Tag; |
11 | use Wallabag\CoreBundle\Helper\TagsAssigner; | ||
11 | use Wallabag\UserBundle\Entity\User; | 12 | use Wallabag\UserBundle\Entity\User; |
12 | use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface; | 13 | use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface; |
13 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; | 14 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
@@ -18,19 +19,22 @@ abstract class AbstractImport implements ImportInterface | |||
18 | protected $em; | 19 | protected $em; |
19 | protected $logger; | 20 | protected $logger; |
20 | protected $contentProxy; | 21 | protected $contentProxy; |
22 | protected $tagsAssigner; | ||
21 | protected $eventDispatcher; | 23 | protected $eventDispatcher; |
22 | protected $producer; | 24 | protected $producer; |
23 | protected $user; | 25 | protected $user; |
24 | protected $markAsRead; | 26 | protected $markAsRead; |
27 | protected $disableContentUpdate = false; | ||
25 | protected $skippedEntries = 0; | 28 | protected $skippedEntries = 0; |
26 | protected $importedEntries = 0; | 29 | protected $importedEntries = 0; |
27 | protected $queuedEntries = 0; | 30 | protected $queuedEntries = 0; |
28 | 31 | ||
29 | public function __construct(EntityManager $em, ContentProxy $contentProxy, EventDispatcherInterface $eventDispatcher) | 32 | public function __construct(EntityManager $em, ContentProxy $contentProxy, TagsAssigner $tagsAssigner, EventDispatcherInterface $eventDispatcher) |
30 | { | 33 | { |
31 | $this->em = $em; | 34 | $this->em = $em; |
32 | $this->logger = new NullLogger(); | 35 | $this->logger = new NullLogger(); |
33 | $this->contentProxy = $contentProxy; | 36 | $this->contentProxy = $contentProxy; |
37 | $this->tagsAssigner = $tagsAssigner; | ||
34 | $this->eventDispatcher = $eventDispatcher; | 38 | $this->eventDispatcher = $eventDispatcher; |
35 | } | 39 | } |
36 | 40 | ||
@@ -82,21 +86,34 @@ abstract class AbstractImport implements ImportInterface | |||
82 | } | 86 | } |
83 | 87 | ||
84 | /** | 88 | /** |
89 | * Set whether articles should be fetched for updated content. | ||
90 | * | ||
91 | * @param bool $disableContentUpdate | ||
92 | */ | ||
93 | public function setDisableContentUpdate($disableContentUpdate) | ||
94 | { | ||
95 | $this->disableContentUpdate = $disableContentUpdate; | ||
96 | |||
97 | return $this; | ||
98 | } | ||
99 | |||
100 | /** | ||
85 | * Fetch content from the ContentProxy (using graby). | 101 | * Fetch content from the ContentProxy (using graby). |
86 | * If it fails return the given entry to be saved in all case (to avoid user to loose the content). | 102 | * If it fails return the given entry to be saved in all case (to avoid user to loose the content). |
87 | * | 103 | * |
88 | * @param Entry $entry Entry to update | 104 | * @param Entry $entry Entry to update |
89 | * @param string $url Url to grab content for | 105 | * @param string $url Url to grab content for |
90 | * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url | 106 | * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url |
91 | * | ||
92 | * @return Entry | ||
93 | */ | 107 | */ |
94 | protected function fetchContent(Entry $entry, $url, array $content = []) | 108 | protected function fetchContent(Entry $entry, $url, array $content = []) |
95 | { | 109 | { |
96 | try { | 110 | try { |
97 | return $this->contentProxy->updateEntry($entry, $url, $content); | 111 | $this->contentProxy->updateEntry($entry, $url, $content, $this->disableContentUpdate); |
98 | } catch (\Exception $e) { | 112 | } catch (\Exception $e) { |
99 | return $entry; | 113 | $this->logger->error('Error trying to import an entry.', [ |
114 | 'entry_url' => $url, | ||
115 | 'error_msg' => $e->getMessage(), | ||
116 | ]); | ||
100 | } | 117 | } |
101 | } | 118 | } |
102 | 119 | ||
diff --git a/src/Wallabag/ImportBundle/Import/BrowserImport.php b/src/Wallabag/ImportBundle/Import/BrowserImport.php index 8bf7d92e..71e65e59 100644 --- a/src/Wallabag/ImportBundle/Import/BrowserImport.php +++ b/src/Wallabag/ImportBundle/Import/BrowserImport.php | |||
@@ -4,7 +4,6 @@ namespace Wallabag\ImportBundle\Import; | |||
4 | 4 | ||
5 | use Wallabag\CoreBundle\Entity\Entry; | 5 | use Wallabag\CoreBundle\Entity\Entry; |
6 | use Wallabag\UserBundle\Entity\User; | 6 | use Wallabag\UserBundle\Entity\User; |
7 | use Wallabag\CoreBundle\Helper\ContentProxy; | ||
8 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | 7 | use Wallabag\CoreBundle\Event\EntrySavedEvent; |
9 | 8 | ||
10 | abstract class BrowserImport extends AbstractImport | 9 | abstract class BrowserImport extends AbstractImport |
@@ -202,10 +201,10 @@ abstract class BrowserImport extends AbstractImport | |||
202 | $entry->setTitle($data['title']); | 201 | $entry->setTitle($data['title']); |
203 | 202 | ||
204 | // update entry with content (in case fetching failed, the given entry will be return) | 203 | // update entry with content (in case fetching failed, the given entry will be return) |
205 | $entry = $this->fetchContent($entry, $data['url'], $data); | 204 | $this->fetchContent($entry, $data['url'], $data); |
206 | 205 | ||
207 | if (array_key_exists('tags', $data)) { | 206 | if (array_key_exists('tags', $data)) { |
208 | $this->contentProxy->assignTagsToEntry( | 207 | $this->tagsAssigner->assignTagsToEntry( |
209 | $entry, | 208 | $entry, |
210 | $data['tags'] | 209 | $data['tags'] |
211 | ); | 210 | ); |
diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php index 70a53f1a..3aa12f6f 100644 --- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php +++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php | |||
@@ -68,6 +68,14 @@ class InstapaperImport extends AbstractImport | |||
68 | continue; | 68 | continue; |
69 | } | 69 | } |
70 | 70 | ||
71 | // last element in the csv is the folder where the content belong | ||
72 | // BUT it can also be the status (since status = folder in Instapaper) | ||
73 | // and we don't want archive, unread & starred to become a tag | ||
74 | $tags = null; | ||
75 | if (false === in_array($data[3], ['Archive', 'Unread', 'Starred'])) { | ||
76 | $tags = [$data[3]]; | ||
77 | } | ||
78 | |||
71 | $entries[] = [ | 79 | $entries[] = [ |
72 | 'url' => $data[0], | 80 | 'url' => $data[0], |
73 | 'title' => $data[1], | 81 | 'title' => $data[1], |
@@ -75,6 +83,7 @@ class InstapaperImport extends AbstractImport | |||
75 | 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred', | 83 | 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred', |
76 | 'is_starred' => $data[3] === 'Starred', | 84 | 'is_starred' => $data[3] === 'Starred', |
77 | 'html' => false, | 85 | 'html' => false, |
86 | 'tags' => $tags, | ||
78 | ]; | 87 | ]; |
79 | } | 88 | } |
80 | fclose($handle); | 89 | fclose($handle); |
@@ -116,7 +125,15 @@ class InstapaperImport extends AbstractImport | |||
116 | $entry->setTitle($importedEntry['title']); | 125 | $entry->setTitle($importedEntry['title']); |
117 | 126 | ||
118 | // update entry with content (in case fetching failed, the given entry will be return) | 127 | // update entry with content (in case fetching failed, the given entry will be return) |
119 | $entry = $this->fetchContent($entry, $importedEntry['url'], $importedEntry); | 128 | $this->fetchContent($entry, $importedEntry['url'], $importedEntry); |
129 | |||
130 | if (!empty($importedEntry['tags'])) { | ||
131 | $this->tagsAssigner->assignTagsToEntry( | ||
132 | $entry, | ||
133 | $importedEntry['tags'], | ||
134 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() | ||
135 | ); | ||
136 | } | ||
120 | 137 | ||
121 | $entry->setArchived($importedEntry['is_archived']); | 138 | $entry->setArchived($importedEntry['is_archived']); |
122 | $entry->setStarred($importedEntry['is_starred']); | 139 | $entry->setStarred($importedEntry['is_starred']); |
diff --git a/src/Wallabag/ImportBundle/Import/PinboardImport.php b/src/Wallabag/ImportBundle/Import/PinboardImport.php index d9865534..110b0464 100644 --- a/src/Wallabag/ImportBundle/Import/PinboardImport.php +++ b/src/Wallabag/ImportBundle/Import/PinboardImport.php | |||
@@ -109,10 +109,10 @@ class PinboardImport extends AbstractImport | |||
109 | $entry->setTitle($data['title']); | 109 | $entry->setTitle($data['title']); |
110 | 110 | ||
111 | // update entry with content (in case fetching failed, the given entry will be return) | 111 | // update entry with content (in case fetching failed, the given entry will be return) |
112 | $entry = $this->fetchContent($entry, $data['url'], $data); | 112 | $this->fetchContent($entry, $data['url'], $data); |
113 | 113 | ||
114 | if (!empty($data['tags'])) { | 114 | if (!empty($data['tags'])) { |
115 | $this->contentProxy->assignTagsToEntry( | 115 | $this->tagsAssigner->assignTagsToEntry( |
116 | $entry, | 116 | $entry, |
117 | $data['tags'], | 117 | $data['tags'], |
118 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() | 118 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() |
diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php index 33093480..c1d5b6da 100644 --- a/src/Wallabag/ImportBundle/Import/PocketImport.php +++ b/src/Wallabag/ImportBundle/Import/PocketImport.php | |||
@@ -5,7 +5,6 @@ namespace Wallabag\ImportBundle\Import; | |||
5 | use GuzzleHttp\Client; | 5 | use GuzzleHttp\Client; |
6 | use GuzzleHttp\Exception\RequestException; | 6 | use GuzzleHttp\Exception\RequestException; |
7 | use Wallabag\CoreBundle\Entity\Entry; | 7 | use Wallabag\CoreBundle\Entity\Entry; |
8 | use Wallabag\CoreBundle\Helper\ContentProxy; | ||
9 | 8 | ||
10 | class PocketImport extends AbstractImport | 9 | class PocketImport extends AbstractImport |
11 | { | 10 | { |
@@ -193,7 +192,7 @@ class PocketImport extends AbstractImport | |||
193 | $entry->setUrl($url); | 192 | $entry->setUrl($url); |
194 | 193 | ||
195 | // update entry with content (in case fetching failed, the given entry will be return) | 194 | // update entry with content (in case fetching failed, the given entry will be return) |
196 | $entry = $this->fetchContent($entry, $url); | 195 | $this->fetchContent($entry, $url); |
197 | 196 | ||
198 | // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted | 197 | // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted |
199 | $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead); | 198 | $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead); |
@@ -216,7 +215,7 @@ class PocketImport extends AbstractImport | |||
216 | } | 215 | } |
217 | 216 | ||
218 | if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) { | 217 | if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) { |
219 | $this->contentProxy->assignTagsToEntry( | 218 | $this->tagsAssigner->assignTagsToEntry( |
220 | $entry, | 219 | $entry, |
221 | array_keys($importedEntry['tags']), | 220 | array_keys($importedEntry['tags']), |
222 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() | 221 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() |
diff --git a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php index de320d23..002b27f4 100644 --- a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php +++ b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php | |||
@@ -109,7 +109,7 @@ class ReadabilityImport extends AbstractImport | |||
109 | $entry->setTitle($data['title']); | 109 | $entry->setTitle($data['title']); |
110 | 110 | ||
111 | // update entry with content (in case fetching failed, the given entry will be return) | 111 | // update entry with content (in case fetching failed, the given entry will be return) |
112 | $entry = $this->fetchContent($entry, $data['url'], $data); | 112 | $this->fetchContent($entry, $data['url'], $data); |
113 | 113 | ||
114 | $entry->setArchived($data['is_archived']); | 114 | $entry->setArchived($data['is_archived']); |
115 | $entry->setStarred($data['is_starred']); | 115 | $entry->setStarred($data['is_starred']); |
diff --git a/src/Wallabag/ImportBundle/Import/WallabagImport.php b/src/Wallabag/ImportBundle/Import/WallabagImport.php index 702da057..c64ccd64 100644 --- a/src/Wallabag/ImportBundle/Import/WallabagImport.php +++ b/src/Wallabag/ImportBundle/Import/WallabagImport.php | |||
@@ -108,10 +108,10 @@ abstract class WallabagImport extends AbstractImport | |||
108 | $entry->setTitle($data['title']); | 108 | $entry->setTitle($data['title']); |
109 | 109 | ||
110 | // update entry with content (in case fetching failed, the given entry will be return) | 110 | // update entry with content (in case fetching failed, the given entry will be return) |
111 | $entry = $this->fetchContent($entry, $data['url'], $data); | 111 | $this->fetchContent($entry, $data['url'], $data); |
112 | 112 | ||
113 | if (array_key_exists('tags', $data)) { | 113 | if (array_key_exists('tags', $data)) { |
114 | $this->contentProxy->assignTagsToEntry( | 114 | $this->tagsAssigner->assignTagsToEntry( |
115 | $entry, | 115 | $entry, |
116 | $data['tags'], | 116 | $data['tags'], |
117 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() | 117 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() |
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php index 59e3ce02..1f0df646 100644 --- a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php +++ b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php | |||
@@ -4,6 +4,17 @@ namespace Wallabag\ImportBundle\Import; | |||
4 | 4 | ||
5 | class WallabagV1Import extends WallabagImport | 5 | class WallabagV1Import extends WallabagImport |
6 | { | 6 | { |
7 | protected $fetchingErrorMessage; | ||
8 | protected $fetchingErrorMessageTitle; | ||
9 | |||
10 | public function __construct($em, $contentProxy, $tagsAssigner, $eventDispatcher, $fetchingErrorMessageTitle, $fetchingErrorMessage) | ||
11 | { | ||
12 | $this->fetchingErrorMessageTitle = $fetchingErrorMessageTitle; | ||
13 | $this->fetchingErrorMessage = $fetchingErrorMessage; | ||
14 | |||
15 | parent::__construct($em, $contentProxy, $tagsAssigner, $eventDispatcher); | ||
16 | } | ||
17 | |||
7 | /** | 18 | /** |
8 | * {@inheritdoc} | 19 | * {@inheritdoc} |
9 | */ | 20 | */ |
@@ -43,10 +54,11 @@ class WallabagV1Import extends WallabagImport | |||
43 | 'created_at' => '', | 54 | 'created_at' => '', |
44 | ]; | 55 | ]; |
45 | 56 | ||
46 | // force content to be refreshed in case on bad fetch in the v1 installation | 57 | // In case of a bad fetch in v1, replace title and content with v2 error strings |
58 | // If fetching fails again, they will get this instead of the v1 strings | ||
47 | if (in_array($entry['title'], $this->untitled)) { | 59 | if (in_array($entry['title'], $this->untitled)) { |
48 | $data['title'] = ''; | 60 | $data['title'] = $this->fetchingErrorMessageTitle; |
49 | $data['html'] = ''; | 61 | $data['html'] = $this->fetchingErrorMessage; |
50 | } | 62 | } |
51 | 63 | ||
52 | if (array_key_exists('tags', $entry) && $entry['tags'] != '') { | 64 | if (array_key_exists('tags', $entry) && $entry['tags'] != '') { |
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php index d2a89d79..3e085ecf 100644 --- a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php +++ b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php | |||
@@ -36,8 +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' => (int) ($entry['is_archived'] || $this->markAsRead), | 39 | 'is_archived' => (bool) ($entry['is_archived'] || $this->markAsRead), |
40 | 'is_starred' => false, | 40 | 'is_starred' => (bool) $entry['is_starred'], |
41 | ] + $entry; | 41 | ] + $entry; |
42 | } | 42 | } |
43 | 43 | ||
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml index c4fe3f92..b224a6a2 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 | - "@wallabag_core.tags_assigner" | ||
23 | - "@event_dispatcher" | 24 | - "@event_dispatcher" |
24 | calls: | 25 | calls: |
25 | - [ setClient, [ "@wallabag_import.pocket.client" ] ] | 26 | - [ setClient, [ "@wallabag_import.pocket.client" ] ] |
@@ -32,7 +33,10 @@ services: | |||
32 | arguments: | 33 | arguments: |
33 | - "@doctrine.orm.entity_manager" | 34 | - "@doctrine.orm.entity_manager" |
34 | - "@wallabag_core.content_proxy" | 35 | - "@wallabag_core.content_proxy" |
36 | - "@wallabag_core.tags_assigner" | ||
35 | - "@event_dispatcher" | 37 | - "@event_dispatcher" |
38 | - "%wallabag_core.fetching_error_message_title%" | ||
39 | - "%wallabag_core.fetching_error_message%" | ||
36 | calls: | 40 | calls: |
37 | - [ setLogger, [ "@logger" ]] | 41 | - [ setLogger, [ "@logger" ]] |
38 | tags: | 42 | tags: |
@@ -43,6 +47,7 @@ services: | |||
43 | arguments: | 47 | arguments: |
44 | - "@doctrine.orm.entity_manager" | 48 | - "@doctrine.orm.entity_manager" |
45 | - "@wallabag_core.content_proxy" | 49 | - "@wallabag_core.content_proxy" |
50 | - "@wallabag_core.tags_assigner" | ||
46 | - "@event_dispatcher" | 51 | - "@event_dispatcher" |
47 | calls: | 52 | calls: |
48 | - [ setLogger, [ "@logger" ]] | 53 | - [ setLogger, [ "@logger" ]] |
@@ -54,6 +59,7 @@ services: | |||
54 | arguments: | 59 | arguments: |
55 | - "@doctrine.orm.entity_manager" | 60 | - "@doctrine.orm.entity_manager" |
56 | - "@wallabag_core.content_proxy" | 61 | - "@wallabag_core.content_proxy" |
62 | - "@wallabag_core.tags_assigner" | ||
57 | - "@event_dispatcher" | 63 | - "@event_dispatcher" |
58 | calls: | 64 | calls: |
59 | - [ setLogger, [ "@logger" ]] | 65 | - [ setLogger, [ "@logger" ]] |
@@ -65,6 +71,7 @@ services: | |||
65 | arguments: | 71 | arguments: |
66 | - "@doctrine.orm.entity_manager" | 72 | - "@doctrine.orm.entity_manager" |
67 | - "@wallabag_core.content_proxy" | 73 | - "@wallabag_core.content_proxy" |
74 | - "@wallabag_core.tags_assigner" | ||
68 | - "@event_dispatcher" | 75 | - "@event_dispatcher" |
69 | calls: | 76 | calls: |
70 | - [ setLogger, [ "@logger" ]] | 77 | - [ setLogger, [ "@logger" ]] |
@@ -76,6 +83,7 @@ services: | |||
76 | arguments: | 83 | arguments: |
77 | - "@doctrine.orm.entity_manager" | 84 | - "@doctrine.orm.entity_manager" |
78 | - "@wallabag_core.content_proxy" | 85 | - "@wallabag_core.content_proxy" |
86 | - "@wallabag_core.tags_assigner" | ||
79 | - "@event_dispatcher" | 87 | - "@event_dispatcher" |
80 | calls: | 88 | calls: |
81 | - [ setLogger, [ "@logger" ]] | 89 | - [ setLogger, [ "@logger" ]] |
@@ -87,6 +95,7 @@ services: | |||
87 | arguments: | 95 | arguments: |
88 | - "@doctrine.orm.entity_manager" | 96 | - "@doctrine.orm.entity_manager" |
89 | - "@wallabag_core.content_proxy" | 97 | - "@wallabag_core.content_proxy" |
98 | - "@wallabag_core.tags_assigner" | ||
90 | - "@event_dispatcher" | 99 | - "@event_dispatcher" |
91 | calls: | 100 | calls: |
92 | - [ setLogger, [ "@logger" ]] | 101 | - [ setLogger, [ "@logger" ]] |
@@ -97,6 +106,7 @@ services: | |||
97 | arguments: | 106 | arguments: |
98 | - "@doctrine.orm.entity_manager" | 107 | - "@doctrine.orm.entity_manager" |
99 | - "@wallabag_core.content_proxy" | 108 | - "@wallabag_core.content_proxy" |
109 | - "@wallabag_core.tags_assigner" | ||
100 | - "@event_dispatcher" | 110 | - "@event_dispatcher" |
101 | calls: | 111 | calls: |
102 | - [ setLogger, [ "@logger" ]] | 112 | - [ setLogger, [ "@logger" ]] |
diff --git a/src/Wallabag/UserBundle/Controller/ManageController.php b/src/Wallabag/UserBundle/Controller/ManageController.php index 92ee2b41..084f2c67 100644 --- a/src/Wallabag/UserBundle/Controller/ManageController.php +++ b/src/Wallabag/UserBundle/Controller/ManageController.php | |||
@@ -4,12 +4,15 @@ namespace Wallabag\UserBundle\Controller; | |||
4 | 4 | ||
5 | use FOS\UserBundle\Event\UserEvent; | 5 | use FOS\UserBundle\Event\UserEvent; |
6 | use FOS\UserBundle\FOSUserEvents; | 6 | use FOS\UserBundle\FOSUserEvents; |
7 | use Pagerfanta\Adapter\DoctrineORMAdapter; | ||
8 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | ||
9 | use Pagerfanta\Pagerfanta; | ||
7 | use Symfony\Component\HttpFoundation\Request; | 10 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 11 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | 12 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; |
10 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 13 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
11 | use Wallabag\UserBundle\Entity\User; | 14 | use Wallabag\UserBundle\Entity\User; |
12 | use Wallabag\CoreBundle\Entity\Config; | 15 | use Wallabag\UserBundle\Form\SearchUserType; |
13 | 16 | ||
14 | /** | 17 | /** |
15 | * User controller. | 18 | * User controller. |
@@ -17,23 +20,6 @@ use Wallabag\CoreBundle\Entity\Config; | |||
17 | class ManageController extends Controller | 20 | class ManageController extends Controller |
18 | { | 21 | { |
19 | /** | 22 | /** |
20 | * Lists all User entities. | ||
21 | * | ||
22 | * @Route("/", name="user_index") | ||
23 | * @Method("GET") | ||
24 | */ | ||
25 | public function indexAction() | ||
26 | { | ||
27 | $em = $this->getDoctrine()->getManager(); | ||
28 | |||
29 | $users = $em->getRepository('WallabagUserBundle:User')->findAll(); | ||
30 | |||
31 | return $this->render('WallabagUserBundle:Manage:index.html.twig', array( | ||
32 | 'users' => $users, | ||
33 | )); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Creates a new User entity. | 23 | * Creates a new User entity. |
38 | * | 24 | * |
39 | * @Route("/new", name="user_new") | 25 | * @Route("/new", name="user_new") |
@@ -47,9 +33,7 @@ class ManageController extends Controller | |||
47 | // enable created user by default | 33 | // enable created user by default |
48 | $user->setEnabled(true); | 34 | $user->setEnabled(true); |
49 | 35 | ||
50 | $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ | 36 | $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user); |
51 | 'validation_groups' => ['Profile'], | ||
52 | ]); | ||
53 | $form->handleRequest($request); | 37 | $form->handleRequest($request); |
54 | 38 | ||
55 | if ($form->isSubmitted() && $form->isValid()) { | 39 | if ($form->isSubmitted() && $form->isValid()) { |
@@ -146,4 +130,49 @@ class ManageController extends Controller | |||
146 | ->getForm() | 130 | ->getForm() |
147 | ; | 131 | ; |
148 | } | 132 | } |
133 | |||
134 | /** | ||
135 | * @param Request $request | ||
136 | * @param int $page | ||
137 | * | ||
138 | * @Route("/list/{page}", name="user_index", defaults={"page" = 1}) | ||
139 | * | ||
140 | * Default parameter for page is hardcoded (in duplication of the defaults from the Route) | ||
141 | * because this controller is also called inside the layout template without any page as argument | ||
142 | * | ||
143 | * @return \Symfony\Component\HttpFoundation\Response | ||
144 | */ | ||
145 | public function searchFormAction(Request $request, $page = 1) | ||
146 | { | ||
147 | $em = $this->getDoctrine()->getManager(); | ||
148 | $qb = $em->getRepository('WallabagUserBundle:User')->createQueryBuilder('u'); | ||
149 | |||
150 | $form = $this->createForm(SearchUserType::class); | ||
151 | $form->handleRequest($request); | ||
152 | |||
153 | if ($form->isSubmitted() && $form->isValid()) { | ||
154 | $this->get('logger')->info('searching users'); | ||
155 | |||
156 | $searchTerm = (isset($request->get('search_user')['term']) ? $request->get('search_user')['term'] : ''); | ||
157 | |||
158 | $qb = $em->getRepository('WallabagUserBundle:User')->getQueryBuilderForSearch($searchTerm); | ||
159 | } | ||
160 | |||
161 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
162 | $pagerFanta = new Pagerfanta($pagerAdapter); | ||
163 | $pagerFanta->setMaxPerPage(50); | ||
164 | |||
165 | try { | ||
166 | $pagerFanta->setCurrentPage($page); | ||
167 | } catch (OutOfRangeCurrentPageException $e) { | ||
168 | if ($page > 1) { | ||
169 | return $this->redirect($this->generateUrl('user_index', ['page' => $pagerFanta->getNbPages()]), 302); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | return $this->render('WallabagUserBundle:Manage:index.html.twig', [ | ||
174 | 'searchForm' => $form->createView(), | ||
175 | 'users' => $pagerFanta, | ||
176 | ]); | ||
177 | } | ||
149 | } | 178 | } |
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php index 3a167de7..aba76ca7 100644 --- a/src/Wallabag/UserBundle/Entity/User.php +++ b/src/Wallabag/UserBundle/Entity/User.php | |||
@@ -4,11 +4,12 @@ namespace Wallabag\UserBundle\Entity; | |||
4 | 4 | ||
5 | use Doctrine\Common\Collections\ArrayCollection; | 5 | use Doctrine\Common\Collections\ArrayCollection; |
6 | use Doctrine\ORM\Mapping as ORM; | 6 | use Doctrine\ORM\Mapping as ORM; |
7 | use JMS\Serializer\Annotation\Groups; | ||
8 | use JMS\Serializer\Annotation\XmlRoot; | ||
9 | use JMS\Serializer\Annotation\Accessor; | ||
7 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; | 10 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; |
8 | use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; | 11 | use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; |
9 | use FOS\UserBundle\Model\User as BaseUser; | 12 | use FOS\UserBundle\Model\User as BaseUser; |
10 | use JMS\Serializer\Annotation\ExclusionPolicy; | ||
11 | use JMS\Serializer\Annotation\Expose; | ||
12 | use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; | 13 | use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; |
13 | use Symfony\Component\Security\Core\User\UserInterface; | 14 | use Symfony\Component\Security\Core\User\UserInterface; |
14 | use Wallabag\ApiBundle\Entity\Client; | 15 | use Wallabag\ApiBundle\Entity\Client; |
@@ -18,23 +19,25 @@ use Wallabag\CoreBundle\Entity\Entry; | |||
18 | /** | 19 | /** |
19 | * User. | 20 | * User. |
20 | * | 21 | * |
22 | * @XmlRoot("user") | ||
21 | * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") | 23 | * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") |
22 | * @ORM\Table(name="`user`") | 24 | * @ORM\Table(name="`user`") |
23 | * @ORM\HasLifecycleCallbacks() | 25 | * @ORM\HasLifecycleCallbacks() |
24 | * @ExclusionPolicy("all") | ||
25 | * | 26 | * |
26 | * @UniqueEntity("email") | 27 | * @UniqueEntity("email") |
27 | * @UniqueEntity("username") | 28 | * @UniqueEntity("username") |
28 | */ | 29 | */ |
29 | class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface | 30 | class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface |
30 | { | 31 | { |
32 | /** @Serializer\XmlAttribute */ | ||
31 | /** | 33 | /** |
32 | * @var int | 34 | * @var int |
33 | * | 35 | * |
34 | * @Expose | ||
35 | * @ORM\Column(name="id", type="integer") | 36 | * @ORM\Column(name="id", type="integer") |
36 | * @ORM\Id | 37 | * @ORM\Id |
37 | * @ORM\GeneratedValue(strategy="AUTO") | 38 | * @ORM\GeneratedValue(strategy="AUTO") |
39 | * | ||
40 | * @Groups({"user_api", "user_api_with_client"}) | ||
38 | */ | 41 | */ |
39 | protected $id; | 42 | protected $id; |
40 | 43 | ||
@@ -42,20 +45,40 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
42 | * @var string | 45 | * @var string |
43 | * | 46 | * |
44 | * @ORM\Column(name="name", type="text", nullable=true) | 47 | * @ORM\Column(name="name", type="text", nullable=true) |
48 | * | ||
49 | * @Groups({"user_api", "user_api_with_client"}) | ||
45 | */ | 50 | */ |
46 | protected $name; | 51 | protected $name; |
47 | 52 | ||
48 | /** | 53 | /** |
49 | * @var date | 54 | * @var string |
55 | * | ||
56 | * @Groups({"user_api", "user_api_with_client"}) | ||
57 | */ | ||
58 | protected $username; | ||
59 | |||
60 | /** | ||
61 | * @var string | ||
62 | * | ||
63 | * @Groups({"user_api", "user_api_with_client"}) | ||
64 | */ | ||
65 | protected $email; | ||
66 | |||
67 | /** | ||
68 | * @var \DateTime | ||
50 | * | 69 | * |
51 | * @ORM\Column(name="created_at", type="datetime") | 70 | * @ORM\Column(name="created_at", type="datetime") |
71 | * | ||
72 | * @Groups({"user_api", "user_api_with_client"}) | ||
52 | */ | 73 | */ |
53 | protected $createdAt; | 74 | protected $createdAt; |
54 | 75 | ||
55 | /** | 76 | /** |
56 | * @var date | 77 | * @var \DateTime |
57 | * | 78 | * |
58 | * @ORM\Column(name="updated_at", type="datetime") | 79 | * @ORM\Column(name="updated_at", type="datetime") |
80 | * | ||
81 | * @Groups({"user_api", "user_api_with_client"}) | ||
59 | */ | 82 | */ |
60 | protected $updatedAt; | 83 | protected $updatedAt; |
61 | 84 | ||
@@ -75,7 +98,8 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
75 | private $authCode; | 98 | private $authCode; |
76 | 99 | ||
77 | /** | 100 | /** |
78 | * @var bool Enabled yes/no | 101 | * @var bool |
102 | * | ||
79 | * @ORM\Column(type="boolean") | 103 | * @ORM\Column(type="boolean") |
80 | */ | 104 | */ |
81 | private $twoFactorAuthentication = false; | 105 | private $twoFactorAuthentication = false; |
@@ -86,10 +110,20 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
86 | private $trusted; | 110 | private $trusted; |
87 | 111 | ||
88 | /** | 112 | /** |
113 | * @var ArrayCollection | ||
114 | * | ||
89 | * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"}) | 115 | * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"}) |
90 | */ | 116 | */ |
91 | protected $clients; | 117 | protected $clients; |
92 | 118 | ||
119 | /** | ||
120 | * @see getFirstClient() below | ||
121 | * | ||
122 | * @Groups({"user_api_with_client"}) | ||
123 | * @Accessor(getter="getFirstClient") | ||
124 | */ | ||
125 | protected $default_client; | ||
126 | |||
93 | public function __construct() | 127 | public function __construct() |
94 | { | 128 | { |
95 | parent::__construct(); | 129 | parent::__construct(); |
@@ -135,7 +169,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
135 | } | 169 | } |
136 | 170 | ||
137 | /** | 171 | /** |
138 | * @return string | 172 | * @return \DateTime |
139 | */ | 173 | */ |
140 | public function getCreatedAt() | 174 | public function getCreatedAt() |
141 | { | 175 | { |
@@ -143,7 +177,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
143 | } | 177 | } |
144 | 178 | ||
145 | /** | 179 | /** |
146 | * @return string | 180 | * @return \DateTime |
147 | */ | 181 | */ |
148 | public function getUpdatedAt() | 182 | public function getUpdatedAt() |
149 | { | 183 | { |
@@ -266,4 +300,16 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
266 | { | 300 | { |
267 | return $this->clients; | 301 | return $this->clients; |
268 | } | 302 | } |
303 | |||
304 | /** | ||
305 | * Only used by the API when creating a new user it'll also return the first client (which was also created at the same time). | ||
306 | * | ||
307 | * @return Client | ||
308 | */ | ||
309 | public function getFirstClient() | ||
310 | { | ||
311 | if (!empty($this->clients)) { | ||
312 | return $this->clients->first(); | ||
313 | } | ||
314 | } | ||
269 | } | 315 | } |
diff --git a/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php b/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php new file mode 100644 index 00000000..10f13233 --- /dev/null +++ b/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php | |||
@@ -0,0 +1,40 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\UserBundle\EventListener; | ||
4 | |||
5 | use Psr\Log\LoggerInterface; | ||
6 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
7 | use Symfony\Component\HttpFoundation\RequestStack; | ||
8 | use Symfony\Component\Security\Core\AuthenticationEvents; | ||
9 | |||
10 | class AuthenticationFailureListener implements EventSubscriberInterface | ||
11 | { | ||
12 | private $requestStack; | ||
13 | private $logger; | ||
14 | |||
15 | public function __construct(RequestStack $requestStack, LoggerInterface $logger) | ||
16 | { | ||
17 | $this->requestStack = $requestStack; | ||
18 | $this->logger = $logger; | ||
19 | } | ||
20 | |||
21 | /** | ||
22 | * {@inheritdoc} | ||
23 | */ | ||
24 | public static function getSubscribedEvents() | ||
25 | { | ||
26 | return [ | ||
27 | AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure', | ||
28 | ]; | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * On failure, add a custom error in log so server admin can configure fail2ban to block IP from people who try to login too much. | ||
33 | */ | ||
34 | public function onAuthenticationFailure() | ||
35 | { | ||
36 | $request = $this->requestStack->getMasterRequest(); | ||
37 | |||
38 | $this->logger->error('Authentication failure for user "'.$request->request->get('_username').'", from IP "'.$request->getClientIp().'", with UA: "'.$request->server->get('HTTP_USER_AGENT').'".'); | ||
39 | } | ||
40 | } | ||
diff --git a/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php b/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php index 0bdd1cae..e4d55c19 100644 --- a/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php +++ b/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php | |||
@@ -5,7 +5,6 @@ namespace Wallabag\UserBundle\EventListener; | |||
5 | use Doctrine\ORM\EntityManager; | 5 | use Doctrine\ORM\EntityManager; |
6 | use FOS\UserBundle\Event\UserEvent; | 6 | use FOS\UserBundle\Event\UserEvent; |
7 | use FOS\UserBundle\FOSUserEvents; | 7 | use FOS\UserBundle\FOSUserEvents; |
8 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||
9 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; | 8 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
10 | use Wallabag\CoreBundle\Entity\Config; | 9 | use Wallabag\CoreBundle\Entity\Config; |
11 | 10 | ||
@@ -47,7 +46,7 @@ class CreateConfigListener implements EventSubscriberInterface | |||
47 | ]; | 46 | ]; |
48 | } | 47 | } |
49 | 48 | ||
50 | public function createConfig(UserEvent $event, $eventName = null, EventDispatcherInterface $eventDispatcher = null) | 49 | public function createConfig(UserEvent $event) |
51 | { | 50 | { |
52 | $config = new Config($event->getUser()); | 51 | $config = new Config($event->getUser()); |
53 | $config->setTheme($this->theme); | 52 | $config->setTheme($this->theme); |
diff --git a/src/Wallabag/UserBundle/Form/SearchUserType.php b/src/Wallabag/UserBundle/Form/SearchUserType.php new file mode 100644 index 00000000..9ce46ee1 --- /dev/null +++ b/src/Wallabag/UserBundle/Form/SearchUserType.php | |||
@@ -0,0 +1,29 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\UserBundle\Form; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\Extension\Core\Type\TextType; | ||
7 | use Symfony\Component\Form\FormBuilderInterface; | ||
8 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
9 | |||
10 | class SearchUserType 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' => 'user.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/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index f913f52d..6adbe329 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php | |||
@@ -52,4 +52,17 @@ class UserRepository extends EntityRepository | |||
52 | ->getQuery() | 52 | ->getQuery() |
53 | ->getSingleScalarResult(); | 53 | ->getSingleScalarResult(); |
54 | } | 54 | } |
55 | |||
56 | /** | ||
57 | * Retrieves users filtered with a search term. | ||
58 | * | ||
59 | * @param string $term | ||
60 | * | ||
61 | * @return QueryBuilder | ||
62 | */ | ||
63 | public function getQueryBuilderForSearch($term) | ||
64 | { | ||
65 | return $this->createQueryBuilder('u') | ||
66 | ->andWhere('lower(u.username) LIKE lower(:term) OR lower(u.email) LIKE lower(:term) OR lower(u.name) LIKE lower(:term)')->setParameter('term', '%'.$term.'%'); | ||
67 | } | ||
55 | } | 68 | } |
diff --git a/src/Wallabag/UserBundle/Resources/config/services.yml b/src/Wallabag/UserBundle/Resources/config/services.yml index 72f6f12c..d3925de3 100644 --- a/src/Wallabag/UserBundle/Resources/config/services.yml +++ b/src/Wallabag/UserBundle/Resources/config/services.yml | |||
@@ -7,7 +7,7 @@ services: | |||
7 | - "%scheb_two_factor.email.sender_email%" | 7 | - "%scheb_two_factor.email.sender_email%" |
8 | - "%scheb_two_factor.email.sender_name%" | 8 | - "%scheb_two_factor.email.sender_name%" |
9 | - '@=service(''craue_config'').get(''wallabag_support_url'')' | 9 | - '@=service(''craue_config'').get(''wallabag_support_url'')' |
10 | - '@=service(''craue_config'').get(''wallabag_url'')' | 10 | - '%domain_name%' |
11 | 11 | ||
12 | wallabag_user.password_resetting: | 12 | wallabag_user.password_resetting: |
13 | class: Wallabag\UserBundle\EventListener\PasswordResettingListener | 13 | class: Wallabag\UserBundle\EventListener\PasswordResettingListener |
@@ -35,3 +35,11 @@ services: | |||
35 | - "%wallabag_core.list_mode%" | 35 | - "%wallabag_core.list_mode%" |
36 | tags: | 36 | tags: |
37 | - { name: kernel.event_subscriber } | 37 | - { name: kernel.event_subscriber } |
38 | |||
39 | wallabag_user.listener.authentication_failure_event_listener: | ||
40 | class: Wallabag\UserBundle\EventListener\AuthenticationFailureListener | ||
41 | arguments: | ||
42 | - "@request_stack" | ||
43 | - "@logger" | ||
44 | tags: | ||
45 | - { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure } | ||
diff --git a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig index daba29e4..15002632 100644 --- a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig +++ b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig | |||
@@ -7,37 +7,60 @@ | |||
7 | <div class="row"> | 7 | <div class="row"> |
8 | <div class="col s12"> | 8 | <div class="col s12"> |
9 | <div class="card-panel"> | 9 | <div class="card-panel"> |
10 | {% if users.getNbPages > 1 %} | ||
11 | {{ pagerfanta(users, 'twitter_bootstrap_translated', {'proximity': 1}) }} | ||
12 | {% endif %} | ||
10 | <div class="row"> | 13 | <div class="row"> |
11 | <div class="input-field col s12"> | 14 | <div class="col s6"> |
12 | <p class="help">{{ 'user.description'|trans|raw }}</p> | 15 | <p class="help">{{ 'user.description'|trans|raw }}</p> |
16 | </div> | ||
17 | <div class="col s6"> | ||
18 | <div class="input-field"> | ||
19 | <form name="search_users" method="GET" action="{{ path('user_index')}}"> | ||
20 | {% if form_errors(searchForm) %} | ||
21 | <span class="black-text">{{ form_errors(searchForm) }}</span> | ||
22 | {% endif %} | ||
23 | |||
24 | {% if form_errors(searchForm.term) %} | ||
25 | <span class="black-text">{{ form_errors(searchForm.term) }}</span> | ||
26 | {% endif %} | ||
13 | 27 | ||
14 | <table class="bordered"> | 28 | {{ form_widget(searchForm.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'user.search.placeholder'} }) }} |
15 | <thead> | 29 | |
16 | <tr> | 30 | {{ form_rest(searchForm) }} |
17 | <th>{{ 'user.form.username_label'|trans }}</th> | 31 | </form> |
18 | <th>{{ 'user.form.email_label'|trans }}</th> | 32 | </div> |
19 | <th>{{ 'user.form.last_login_label'|trans }}</th> | ||
20 | <th>{{ 'user.list.actions'|trans }}</th> | ||
21 | </tr> | ||
22 | </thead> | ||
23 | <tbody> | ||
24 | {% for user in users %} | ||
25 | <tr> | ||
26 | <td>{{ user.username }}</td> | ||
27 | <td>{{ user.email }}</td> | ||
28 | <td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td> | ||
29 | <td> | ||
30 | <a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a> | ||
31 | </td> | ||
32 | </tr> | ||
33 | {% endfor %} | ||
34 | </tbody> | ||
35 | </table> | ||
36 | <br /> | ||
37 | <p> | ||
38 | <a href="{{ path('user_new') }}" class="waves-effect waves-light btn">{{ 'user.list.create_new_one'|trans }}</a> | ||
39 | </p> | ||
40 | </div> | 33 | </div> |
34 | |||
35 | <table class="bordered"> | ||
36 | <thead> | ||
37 | <tr> | ||
38 | <th>{{ 'user.form.username_label'|trans }}</th> | ||
39 | <th>{{ 'user.form.email_label'|trans }}</th> | ||
40 | <th>{{ 'user.form.last_login_label'|trans }}</th> | ||
41 | <th>{{ 'user.list.actions'|trans }}</th> | ||
42 | </tr> | ||
43 | </thead> | ||
44 | <tbody> | ||
45 | {% for user in users %} | ||
46 | <tr> | ||
47 | <td>{{ user.username }}</td> | ||
48 | <td>{{ user.email }}</td> | ||
49 | <td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td> | ||
50 | <td> | ||
51 | <a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a> | ||
52 | </td> | ||
53 | </tr> | ||
54 | {% endfor %} | ||
55 | </tbody> | ||
56 | </table> | ||
57 | <br /> | ||
58 | <p> | ||
59 | <a href="{{ path('user_new') }}" class="waves-effect waves-light btn">{{ 'user.list.create_new_one'|trans }}</a> | ||
60 | </p> | ||
61 | {% if users.getNbPages > 1 %} | ||
62 | {{ pagerfanta(users, 'twitter_bootstrap_translated', {'proximity': 1}) }} | ||
63 | {% endif %} | ||
41 | </div> | 64 | </div> |
42 | </div> | 65 | </div> |
43 | </div> | 66 | </div> |