diff options
Diffstat (limited to 'src')
164 files changed, 5748 insertions, 1963 deletions
diff --git a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php index c13a034f..f3090e65 100644 --- a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php +++ b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php | |||
@@ -3,10 +3,12 @@ | |||
3 | namespace Wallabag\AnnotationBundle\Controller; | 3 | namespace Wallabag\AnnotationBundle\Controller; |
4 | 4 | ||
5 | use FOS\RestBundle\Controller\FOSRestController; | 5 | use FOS\RestBundle\Controller\FOSRestController; |
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||
6 | use Symfony\Component\HttpFoundation\JsonResponse; | 7 | use Symfony\Component\HttpFoundation\JsonResponse; |
7 | use Symfony\Component\HttpFoundation\Request; | 8 | use Symfony\Component\HttpFoundation\Request; |
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 |
@@ -29,7 +31,7 @@ class WallabagAnnotationController extends FOSRestController | |||
29 | $total = count($annotationRows); | 31 | $total = count($annotationRows); |
30 | $annotations = ['total' => $total, 'rows' => $annotationRows]; | 32 | $annotations = ['total' => $total, 'rows' => $annotationRows]; |
31 | 33 | ||
32 | $json = $this->get('serializer')->serialize($annotations, 'json'); | 34 | $json = $this->get('jms_serializer')->serialize($annotations, 'json'); |
33 | 35 | ||
34 | return (new JsonResponse())->setJson($json); | 36 | return (new JsonResponse())->setJson($json); |
35 | } | 37 | } |
@@ -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('jms_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('jms_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 | /** |
@@ -115,7 +124,7 @@ class WallabagAnnotationController extends FOSRestController | |||
115 | $em->remove($annotation); | 124 | $em->remove($annotation); |
116 | $em->flush(); | 125 | $em->flush(); |
117 | 126 | ||
118 | $json = $this->get('serializer')->serialize($annotation, 'json'); | 127 | $json = $this->get('jms_serializer')->serialize($annotation, 'json'); |
119 | 128 | ||
120 | return (new JsonResponse())->setJson($json); | 129 | return (new JsonResponse())->setJson($json); |
121 | } | 130 | } |
diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php index 0838f5aa..a180d504 100644 --- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php +++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php | |||
@@ -3,13 +3,15 @@ | |||
3 | namespace Wallabag\AnnotationBundle\Entity; | 3 | namespace Wallabag\AnnotationBundle\Entity; |
4 | 4 | ||
5 | use Doctrine\ORM\Mapping as ORM; | 5 | use Doctrine\ORM\Mapping as ORM; |
6 | use JMS\Serializer\Annotation\ExclusionPolicy; | ||
7 | use JMS\Serializer\Annotation\Exclude; | 6 | use JMS\Serializer\Annotation\Exclude; |
8 | use JMS\Serializer\Annotation\VirtualProperty; | 7 | use JMS\Serializer\Annotation\ExclusionPolicy; |
9 | use JMS\Serializer\Annotation\SerializedName; | ||
10 | use JMS\Serializer\Annotation\Groups; | 8 | use JMS\Serializer\Annotation\Groups; |
11 | use Wallabag\UserBundle\Entity\User; | 9 | use JMS\Serializer\Annotation\SerializedName; |
10 | use JMS\Serializer\Annotation\VirtualProperty; | ||
11 | use Symfony\Component\Validator\Constraints as Assert; | ||
12 | use Wallabag\CoreBundle\Entity\Entry; | 12 | use Wallabag\CoreBundle\Entity\Entry; |
13 | use Wallabag\CoreBundle\Helper\EntityTimestampsTrait; | ||
14 | use Wallabag\UserBundle\Entity\User; | ||
13 | 15 | ||
14 | /** | 16 | /** |
15 | * Annotation. | 17 | * Annotation. |
@@ -21,6 +23,8 @@ use Wallabag\CoreBundle\Entity\Entry; | |||
21 | */ | 23 | */ |
22 | class Annotation | 24 | class Annotation |
23 | { | 25 | { |
26 | use EntityTimestampsTrait; | ||
27 | |||
24 | /** | 28 | /** |
25 | * @var int | 29 | * @var int |
26 | * | 30 | * |
@@ -56,7 +60,11 @@ class Annotation | |||
56 | /** | 60 | /** |
57 | * @var string | 61 | * @var string |
58 | * | 62 | * |
59 | * @ORM\Column(name="quote", type="string") | 63 | * @Assert\Length( |
64 | * max = 10000, | ||
65 | * maxMessage = "validator.quote_length_too_high" | ||
66 | * ) | ||
67 | * @ORM\Column(name="quote", type="text") | ||
60 | * | 68 | * |
61 | * @Groups({"entries_for_user", "export_all"}) | 69 | * @Groups({"entries_for_user", "export_all"}) |
62 | */ | 70 | */ |
@@ -129,18 +137,6 @@ class Annotation | |||
129 | } | 137 | } |
130 | 138 | ||
131 | /** | 139 | /** |
132 | * @ORM\PrePersist | ||
133 | * @ORM\PreUpdate | ||
134 | */ | ||
135 | public function timestamps() | ||
136 | { | ||
137 | if (is_null($this->createdAt)) { | ||
138 | $this->createdAt = new \DateTime(); | ||
139 | } | ||
140 | $this->updatedAt = new \DateTime(); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Get created. | 140 | * Get created. |
145 | * | 141 | * |
146 | * @return \DateTime | 142 | * @return \DateTime |
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..b44f7e64 100644 --- a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php +++ b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php | |||
@@ -3,6 +3,8 @@ | |||
3 | namespace Wallabag\AnnotationBundle\Repository; | 3 | namespace Wallabag\AnnotationBundle\Repository; |
4 | 4 | ||
5 | use Doctrine\ORM\EntityRepository; | 5 | use Doctrine\ORM\EntityRepository; |
6 | use Doctrine\ORM\QueryBuilder; | ||
7 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
6 | 8 | ||
7 | /** | 9 | /** |
8 | * AnnotationRepository. | 10 | * AnnotationRepository. |
@@ -10,22 +12,6 @@ use Doctrine\ORM\EntityRepository; | |||
10 | class AnnotationRepository extends EntityRepository | 12 | class AnnotationRepository extends EntityRepository |
11 | { | 13 | { |
12 | /** | 14 | /** |
13 | * Return a query builder to used by other getBuilderFor* method. | ||
14 | * | ||
15 | * @param int $userId | ||
16 | * | ||
17 | * @return QueryBuilder | ||
18 | */ | ||
19 | private function getBuilderByUser($userId) | ||
20 | { | ||
21 | return $this->createQueryBuilder('a') | ||
22 | ->leftJoin('a.user', 'u') | ||
23 | ->andWhere('u.id = :userId')->setParameter('userId', $userId) | ||
24 | ->orderBy('a.id', 'desc') | ||
25 | ; | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Retrieves all annotations for a user. | 15 | * Retrieves all annotations for a user. |
30 | * | 16 | * |
31 | * @param int $userId | 17 | * @param int $userId |
@@ -122,4 +108,37 @@ class AnnotationRepository extends EntityRepository | |||
122 | ->setParameter('userId', $userId) | 108 | ->setParameter('userId', $userId) |
123 | ->execute(); | 109 | ->execute(); |
124 | } | 110 | } |
111 | |||
112 | /** | ||
113 | * Find all annotations related to archived entries. | ||
114 | * | ||
115 | * @param $userId | ||
116 | * | ||
117 | * @return mixed | ||
118 | */ | ||
119 | public function findAllArchivedEntriesByUser($userId) | ||
120 | { | ||
121 | return $this->createQueryBuilder('a') | ||
122 | ->leftJoin('a.entry', 'e') | ||
123 | ->where('a.user = :userid')->setParameter(':userid', $userId) | ||
124 | ->andWhere('e.isArchived = true') | ||
125 | ->getQuery() | ||
126 | ->getResult(); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * Return a query builder to used by other getBuilderFor* method. | ||
131 | * | ||
132 | * @param int $userId | ||
133 | * | ||
134 | * @return QueryBuilder | ||
135 | */ | ||
136 | private function getBuilderByUser($userId) | ||
137 | { | ||
138 | return $this->createQueryBuilder('a') | ||
139 | ->leftJoin('a.user', 'u') | ||
140 | ->andWhere('u.id = :userId')->setParameter('userId', $userId) | ||
141 | ->orderBy('a.id', 'desc') | ||
142 | ; | ||
143 | } | ||
125 | } | 144 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php index 2dd26c07..28d55ba9 100644 --- a/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php +++ b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php | |||
@@ -4,10 +4,10 @@ namespace Wallabag\ApiBundle\Controller; | |||
4 | 4 | ||
5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | 6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Symfony\Component\HttpFoundation\JsonResponse; | 7 | use Symfony\Component\HttpFoundation\JsonResponse; |
9 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Symfony\Component\HttpFoundation\Request; |
10 | use Wallabag\AnnotationBundle\Entity\Annotation; | 9 | use Wallabag\AnnotationBundle\Entity\Annotation; |
10 | use Wallabag\CoreBundle\Entity\Entry; | ||
11 | 11 | ||
12 | class AnnotationRestController extends WallabagRestController | 12 | class AnnotationRestController extends WallabagRestController |
13 | { | 13 | { |
diff --git a/src/Wallabag/ApiBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php index 9cb1b626..c7178017 100644 --- a/src/Wallabag/ApiBundle/Controller/DeveloperController.php +++ b/src/Wallabag/ApiBundle/Controller/DeveloperController.php | |||
@@ -3,8 +3,8 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Wallabag\ApiBundle\Entity\Client; | 8 | use Wallabag\ApiBundle\Entity\Client; |
9 | use Wallabag\ApiBundle\Form\Type\ClientType; | 9 | use Wallabag\ApiBundle\Form\Type\ClientType; |
10 | 10 | ||
@@ -75,7 +75,7 @@ class DeveloperController extends Controller | |||
75 | */ | 75 | */ |
76 | public function deleteClientAction(Client $client) | 76 | public function deleteClientAction(Client $client) |
77 | { | 77 | { |
78 | if (null === $this->getUser() || $client->getUser()->getId() != $this->getUser()->getId()) { | 78 | if (null === $this->getUser() || $client->getUser()->getId() !== $this->getUser()->getId()) { |
79 | throw $this->createAccessDeniedException('You can not access this client.'); | 79 | throw $this->createAccessDeniedException('You can not access this client.'); |
80 | } | 80 | } |
81 | 81 | ||
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 54c1747c..6f161a08 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -4,22 +4,30 @@ namespace Wallabag\ApiBundle\Controller; | |||
4 | 4 | ||
5 | use Hateoas\Configuration\Route; | 5 | use Hateoas\Configuration\Route; |
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | 6 | use Hateoas\Representation\Factory\PagerfantaFactory; |
7 | use JMS\Serializer\SerializationContext; | ||
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
8 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Symfony\Component\HttpFoundation\JsonResponse; | 9 | use Symfony\Component\HttpFoundation\JsonResponse; |
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Symfony\Component\HttpFoundation\Response; | ||
12 | use Symfony\Component\HttpKernel\Exception\HttpException; | ||
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 13 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
11 | use Wallabag\CoreBundle\Entity\Entry; | 14 | use Wallabag\CoreBundle\Entity\Entry; |
12 | use Wallabag\CoreBundle\Entity\Tag; | 15 | use Wallabag\CoreBundle\Entity\Tag; |
13 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
14 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | 16 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; |
17 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
15 | 18 | ||
16 | class EntryRestController extends WallabagRestController | 19 | class EntryRestController extends WallabagRestController |
17 | { | 20 | { |
18 | /** | 21 | /** |
19 | * Check if an entry exist by url. | 22 | * Check if an entry exist by url. |
23 | * Return ID if entry(ies) exist (and if you give the return_id parameter). | ||
24 | * Otherwise it returns false. | ||
25 | * | ||
26 | * @todo Remove that `return_id` in the next major release | ||
20 | * | 27 | * |
21 | * @ApiDoc( | 28 | * @ApiDoc( |
22 | * parameters={ | 29 | * parameters={ |
30 | * {"name"="return_id", "dataType"="string", "required"=false, "format"="1 or 0", "description"="Set 1 if you want to retrieve ID in case entry(ies) exists, 0 by default"}, | ||
23 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | 31 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, |
24 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} | 32 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} |
25 | * } | 33 | * } |
@@ -31,6 +39,7 @@ class EntryRestController extends WallabagRestController | |||
31 | { | 39 | { |
32 | $this->validateAuthentication(); | 40 | $this->validateAuthentication(); |
33 | 41 | ||
42 | $returnId = (null === $request->query->get('return_id')) ? false : (bool) $request->query->get('return_id'); | ||
34 | $urls = $request->query->get('urls', []); | 43 | $urls = $request->query->get('urls', []); |
35 | 44 | ||
36 | // handle multiple urls first | 45 | // handle multiple urls first |
@@ -41,30 +50,26 @@ class EntryRestController extends WallabagRestController | |||
41 | ->getRepository('WallabagCoreBundle:Entry') | 50 | ->getRepository('WallabagCoreBundle:Entry') |
42 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 51 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
43 | 52 | ||
44 | $results[$url] = false === $res ? false : true; | 53 | $results[$url] = $this->returnExistInformation($res, $returnId); |
45 | } | 54 | } |
46 | 55 | ||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | 56 | return $this->sendResponse($results); |
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | 57 | } |
51 | 58 | ||
52 | // let's see if it is a simple url? | 59 | // let's see if it is a simple url? |
53 | $url = $request->query->get('url', ''); | 60 | $url = $request->query->get('url', ''); |
54 | 61 | ||
55 | if (empty($url)) { | 62 | if (empty($url)) { |
56 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | 63 | throw $this->createAccessDeniedException('URL is empty?, logged user id: ' . $this->getUser()->getId()); |
57 | } | 64 | } |
58 | 65 | ||
59 | $res = $this->getDoctrine() | 66 | $res = $this->getDoctrine() |
60 | ->getRepository('WallabagCoreBundle:Entry') | 67 | ->getRepository('WallabagCoreBundle:Entry') |
61 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 68 | ->findByUrlAndUserId($url, $this->getUser()->getId()); |
62 | 69 | ||
63 | $exists = false === $res ? false : true; | 70 | $exists = $this->returnExistInformation($res, $returnId); |
64 | |||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
66 | 71 | ||
67 | return (new JsonResponse())->setJson($json); | 72 | return $this->sendResponse(['exists' => $exists]); |
68 | } | 73 | } |
69 | 74 | ||
70 | /** | 75 | /** |
@@ -80,6 +85,7 @@ class EntryRestController extends WallabagRestController | |||
80 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | 85 | * {"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."}, | 86 | * {"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."}, | 87 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, |
88 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"}, | ||
83 | * } | 89 | * } |
84 | * ) | 90 | * ) |
85 | * | 91 | * |
@@ -91,17 +97,25 @@ class EntryRestController extends WallabagRestController | |||
91 | 97 | ||
92 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | 98 | $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'); | 99 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); |
100 | $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public'); | ||
94 | $sort = $request->query->get('sort', 'created'); | 101 | $sort = $request->query->get('sort', 'created'); |
95 | $order = $request->query->get('order', 'desc'); | 102 | $order = $request->query->get('order', 'desc'); |
96 | $page = (int) $request->query->get('page', 1); | 103 | $page = (int) $request->query->get('page', 1); |
97 | $perPage = (int) $request->query->get('perPage', 30); | 104 | $perPage = (int) $request->query->get('perPage', 30); |
98 | $tags = $request->query->get('tags', ''); | 105 | $tags = is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', ''); |
99 | $since = $request->query->get('since', 0); | 106 | $since = $request->query->get('since', 0); |
100 | 107 | ||
101 | /** @var \Pagerfanta\Pagerfanta $pager */ | 108 | /** @var \Pagerfanta\Pagerfanta $pager */ |
102 | $pager = $this->getDoctrine() | 109 | $pager = $this->get('wallabag_core.entry_repository')->findEntries( |
103 | ->getRepository('WallabagCoreBundle:Entry') | 110 | $this->getUser()->getId(), |
104 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | 111 | $isArchived, |
112 | $isStarred, | ||
113 | $isPublic, | ||
114 | $sort, | ||
115 | $order, | ||
116 | $since, | ||
117 | $tags | ||
118 | ); | ||
105 | 119 | ||
106 | $pager->setMaxPerPage($perPage); | 120 | $pager->setMaxPerPage($perPage); |
107 | $pager->setCurrentPage($page); | 121 | $pager->setCurrentPage($page); |
@@ -114,6 +128,7 @@ class EntryRestController extends WallabagRestController | |||
114 | [ | 128 | [ |
115 | 'archive' => $isArchived, | 129 | 'archive' => $isArchived, |
116 | 'starred' => $isStarred, | 130 | 'starred' => $isStarred, |
131 | 'public' => $isPublic, | ||
117 | 'sort' => $sort, | 132 | 'sort' => $sort, |
118 | 'order' => $order, | 133 | 'order' => $order, |
119 | 'page' => $page, | 134 | 'page' => $page, |
@@ -125,9 +140,7 @@ class EntryRestController extends WallabagRestController | |||
125 | ) | 140 | ) |
126 | ); | 141 | ); |
127 | 142 | ||
128 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | 143 | return $this->sendResponse($paginatedCollection); |
129 | |||
130 | return (new JsonResponse())->setJson($json); | ||
131 | } | 144 | } |
132 | 145 | ||
133 | /** | 146 | /** |
@@ -146,9 +159,7 @@ class EntryRestController extends WallabagRestController | |||
146 | $this->validateAuthentication(); | 159 | $this->validateAuthentication(); |
147 | $this->validateUserAccess($entry->getUser()->getId()); | 160 | $this->validateUserAccess($entry->getUser()->getId()); |
148 | 161 | ||
149 | $json = $this->get('serializer')->serialize($entry, 'json'); | 162 | return $this->sendResponse($entry); |
150 | |||
151 | return (new JsonResponse())->setJson($json); | ||
152 | } | 163 | } |
153 | 164 | ||
154 | /** | 165 | /** |
@@ -170,19 +181,134 @@ class EntryRestController extends WallabagRestController | |||
170 | return $this->get('wallabag_core.helper.entries_export') | 181 | return $this->get('wallabag_core.helper.entries_export') |
171 | ->setEntries($entry) | 182 | ->setEntries($entry) |
172 | ->updateTitle('entry') | 183 | ->updateTitle('entry') |
184 | ->updateAuthor('entry') | ||
173 | ->exportAs($request->attributes->get('_format')); | 185 | ->exportAs($request->attributes->get('_format')); |
174 | } | 186 | } |
175 | 187 | ||
176 | /** | 188 | /** |
189 | * Handles an entries list and delete URL. | ||
190 | * | ||
191 | * @ApiDoc( | ||
192 | * parameters={ | ||
193 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} | ||
194 | * } | ||
195 | * ) | ||
196 | * | ||
197 | * @return JsonResponse | ||
198 | */ | ||
199 | public function deleteEntriesListAction(Request $request) | ||
200 | { | ||
201 | $this->validateAuthentication(); | ||
202 | |||
203 | $urls = json_decode($request->query->get('urls', [])); | ||
204 | |||
205 | if (empty($urls)) { | ||
206 | return $this->sendResponse([]); | ||
207 | } | ||
208 | |||
209 | $results = []; | ||
210 | |||
211 | // handle multiple urls | ||
212 | foreach ($urls as $key => $url) { | ||
213 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
214 | $url, | ||
215 | $this->getUser()->getId() | ||
216 | ); | ||
217 | |||
218 | $results[$key]['url'] = $url; | ||
219 | |||
220 | if (false !== $entry) { | ||
221 | $em = $this->getDoctrine()->getManager(); | ||
222 | $em->remove($entry); | ||
223 | $em->flush(); | ||
224 | |||
225 | // entry deleted, dispatch event about it! | ||
226 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
227 | } | ||
228 | |||
229 | $results[$key]['entry'] = $entry instanceof Entry ? true : false; | ||
230 | } | ||
231 | |||
232 | return $this->sendResponse($results); | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * Handles an entries list and create URL. | ||
237 | * | ||
238 | * @ApiDoc( | ||
239 | * parameters={ | ||
240 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} | ||
241 | * } | ||
242 | * ) | ||
243 | * | ||
244 | * @throws HttpException When limit is reached | ||
245 | * | ||
246 | * @return JsonResponse | ||
247 | */ | ||
248 | public function postEntriesListAction(Request $request) | ||
249 | { | ||
250 | $this->validateAuthentication(); | ||
251 | |||
252 | $urls = json_decode($request->query->get('urls', [])); | ||
253 | |||
254 | $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); | ||
255 | |||
256 | if (count($urls) > $limit) { | ||
257 | throw new HttpException(400, 'API limit reached'); | ||
258 | } | ||
259 | |||
260 | $results = []; | ||
261 | if (empty($urls)) { | ||
262 | return $this->sendResponse($results); | ||
263 | } | ||
264 | |||
265 | // handle multiple urls | ||
266 | foreach ($urls as $key => $url) { | ||
267 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
268 | $url, | ||
269 | $this->getUser()->getId() | ||
270 | ); | ||
271 | |||
272 | $results[$key]['url'] = $url; | ||
273 | |||
274 | if (false === $entry) { | ||
275 | $entry = new Entry($this->getUser()); | ||
276 | |||
277 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url); | ||
278 | } | ||
279 | |||
280 | $em = $this->getDoctrine()->getManager(); | ||
281 | $em->persist($entry); | ||
282 | $em->flush(); | ||
283 | |||
284 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
285 | |||
286 | // entry saved, dispatch event about it! | ||
287 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
288 | } | ||
289 | |||
290 | return $this->sendResponse($results); | ||
291 | } | ||
292 | |||
293 | /** | ||
177 | * Create an entry. | 294 | * Create an entry. |
178 | * | 295 | * |
296 | * 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**. | ||
297 | * Otherwise, content will be fetched as normal from the url and values will be overwritten. | ||
298 | * | ||
179 | * @ApiDoc( | 299 | * @ApiDoc( |
180 | * parameters={ | 300 | * parameters={ |
181 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | 301 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, |
182 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | 302 | * {"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."}, | 303 | * {"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"}, | 304 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, |
305 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
306 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
307 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
308 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
309 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
310 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
311 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, | ||
186 | * } | 312 | * } |
187 | * ) | 313 | * ) |
188 | * | 314 | * |
@@ -193,43 +319,61 @@ class EntryRestController extends WallabagRestController | |||
193 | $this->validateAuthentication(); | 319 | $this->validateAuthentication(); |
194 | 320 | ||
195 | $url = $request->request->get('url'); | 321 | $url = $request->request->get('url'); |
196 | $title = $request->request->get('title'); | ||
197 | $isArchived = $request->request->get('archive'); | ||
198 | $isStarred = $request->request->get('starred'); | ||
199 | 322 | ||
200 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | 323 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
324 | $url, | ||
325 | $this->getUser()->getId() | ||
326 | ); | ||
201 | 327 | ||
202 | if (false === $entry) { | 328 | if (false === $entry) { |
203 | $entry = new Entry($this->getUser()); | 329 | $entry = new Entry($this->getUser()); |
204 | try { | 330 | $entry->setUrl($url); |
205 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
206 | $entry, | ||
207 | $url | ||
208 | ); | ||
209 | } catch (\Exception $e) { | ||
210 | $this->get('logger')->error('Error while saving an entry', [ | ||
211 | 'exception' => $e, | ||
212 | 'entry' => $entry, | ||
213 | ]); | ||
214 | $entry->setUrl($url); | ||
215 | } | ||
216 | } | 331 | } |
217 | 332 | ||
218 | if (!is_null($title)) { | 333 | $data = $this->retrieveValueFromRequest($request); |
219 | $entry->setTitle($title); | 334 | |
335 | try { | ||
336 | $this->get('wallabag_core.content_proxy')->updateEntry( | ||
337 | $entry, | ||
338 | $entry->getUrl(), | ||
339 | [ | ||
340 | 'title' => !empty($data['title']) ? $data['title'] : $entry->getTitle(), | ||
341 | 'html' => !empty($data['content']) ? $data['content'] : $entry->getContent(), | ||
342 | 'url' => $entry->getUrl(), | ||
343 | 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(), | ||
344 | 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(), | ||
345 | // faking the open graph preview picture | ||
346 | 'open_graph' => [ | ||
347 | 'og_image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(), | ||
348 | ], | ||
349 | 'authors' => is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(), | ||
350 | ] | ||
351 | ); | ||
352 | } catch (\Exception $e) { | ||
353 | $this->get('logger')->error('Error while saving an entry', [ | ||
354 | 'exception' => $e, | ||
355 | 'entry' => $entry, | ||
356 | ]); | ||
220 | } | 357 | } |
221 | 358 | ||
222 | $tags = $request->request->get('tags', ''); | 359 | if (null !== $data['isArchived']) { |
223 | if (!empty($tags)) { | 360 | $entry->setArchived((bool) $data['isArchived']); |
224 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 361 | } |
362 | |||
363 | if (null !== $data['isStarred']) { | ||
364 | $entry->updateStar((bool) $data['isStarred']); | ||
225 | } | 365 | } |
226 | 366 | ||
227 | if (!is_null($isStarred)) { | 367 | if (!empty($data['tags'])) { |
228 | $entry->setStarred((bool) $isStarred); | 368 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']); |
229 | } | 369 | } |
230 | 370 | ||
231 | if (!is_null($isArchived)) { | 371 | if (null !== $data['isPublic']) { |
232 | $entry->setArchived((bool) $isArchived); | 372 | if (true === (bool) $data['isPublic'] && null === $entry->getUid()) { |
373 | $entry->generateUid(); | ||
374 | } elseif (false === (bool) $data['isPublic']) { | ||
375 | $entry->cleanUid(); | ||
376 | } | ||
233 | } | 377 | } |
234 | 378 | ||
235 | $em = $this->getDoctrine()->getManager(); | 379 | $em = $this->getDoctrine()->getManager(); |
@@ -239,9 +383,7 @@ class EntryRestController extends WallabagRestController | |||
239 | // entry saved, dispatch event about it! | 383 | // entry saved, dispatch event about it! |
240 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 384 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
241 | 385 | ||
242 | $json = $this->get('serializer')->serialize($entry, 'json'); | 386 | return $this->sendResponse($entry); |
243 | |||
244 | return (new JsonResponse())->setJson($json); | ||
245 | } | 387 | } |
246 | 388 | ||
247 | /** | 389 | /** |
@@ -256,6 +398,12 @@ class EntryRestController extends WallabagRestController | |||
256 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | 398 | * {"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."}, | 399 | * {"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."}, | 400 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, |
401 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, | ||
402 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, | ||
403 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, | ||
404 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, | ||
405 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, | ||
406 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, | ||
259 | * } | 407 | * } |
260 | * ) | 408 | * ) |
261 | * | 409 | * |
@@ -266,33 +414,80 @@ class EntryRestController extends WallabagRestController | |||
266 | $this->validateAuthentication(); | 414 | $this->validateAuthentication(); |
267 | $this->validateUserAccess($entry->getUser()->getId()); | 415 | $this->validateUserAccess($entry->getUser()->getId()); |
268 | 416 | ||
269 | $title = $request->request->get('title'); | 417 | $contentProxy = $this->get('wallabag_core.content_proxy'); |
270 | $isArchived = $request->request->get('archive'); | ||
271 | $isStarred = $request->request->get('starred'); | ||
272 | 418 | ||
273 | if (!is_null($title)) { | 419 | $data = $this->retrieveValueFromRequest($request); |
274 | $entry->setTitle($title); | 420 | |
421 | // this is a special case where user want to manually update the entry content | ||
422 | // the ContentProxy will only cleanup the html | ||
423 | // and also we force to not re-fetch the content in case of error | ||
424 | if (!empty($data['content'])) { | ||
425 | try { | ||
426 | $contentProxy->updateEntry( | ||
427 | $entry, | ||
428 | $entry->getUrl(), | ||
429 | [ | ||
430 | 'html' => $data['content'], | ||
431 | ], | ||
432 | true | ||
433 | ); | ||
434 | } catch (\Exception $e) { | ||
435 | $this->get('logger')->error('Error while saving an entry', [ | ||
436 | 'exception' => $e, | ||
437 | 'entry' => $entry, | ||
438 | ]); | ||
439 | } | ||
275 | } | 440 | } |
276 | 441 | ||
277 | if (!is_null($isArchived)) { | 442 | if (!empty($data['title'])) { |
278 | $entry->setArchived((bool) $isArchived); | 443 | $entry->setTitle($data['title']); |
279 | } | 444 | } |
280 | 445 | ||
281 | if (!is_null($isStarred)) { | 446 | if (!empty($data['language'])) { |
282 | $entry->setStarred((bool) $isStarred); | 447 | $contentProxy->updateLanguage($entry, $data['language']); |
283 | } | 448 | } |
284 | 449 | ||
285 | $tags = $request->request->get('tags', ''); | 450 | if (!empty($data['authors']) && is_string($data['authors'])) { |
286 | if (!empty($tags)) { | 451 | $entry->setPublishedBy(explode(',', $data['authors'])); |
287 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 452 | } |
453 | |||
454 | if (!empty($data['picture'])) { | ||
455 | $contentProxy->updatePreviewPicture($entry, $data['picture']); | ||
456 | } | ||
457 | |||
458 | if (!empty($data['publishedAt'])) { | ||
459 | $contentProxy->updatePublishedAt($entry, $data['publishedAt']); | ||
460 | } | ||
461 | |||
462 | if (null !== $data['isArchived']) { | ||
463 | $entry->setArchived((bool) $data['isArchived']); | ||
464 | } | ||
465 | |||
466 | if (null !== $data['isStarred']) { | ||
467 | $entry->updateStar((bool) $data['isStarred']); | ||
468 | } | ||
469 | |||
470 | if (!empty($data['tags'])) { | ||
471 | $entry->removeAllTags(); | ||
472 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']); | ||
473 | } | ||
474 | |||
475 | if (null !== $data['isPublic']) { | ||
476 | if (true === (bool) $data['isPublic'] && null === $entry->getUid()) { | ||
477 | $entry->generateUid(); | ||
478 | } elseif (false === (bool) $data['isPublic']) { | ||
479 | $entry->cleanUid(); | ||
480 | } | ||
288 | } | 481 | } |
289 | 482 | ||
290 | $em = $this->getDoctrine()->getManager(); | 483 | $em = $this->getDoctrine()->getManager(); |
484 | $em->persist($entry); | ||
291 | $em->flush(); | 485 | $em->flush(); |
292 | 486 | ||
293 | $json = $this->get('serializer')->serialize($entry, 'json'); | 487 | // entry saved, dispatch event about it! |
488 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
294 | 489 | ||
295 | return (new JsonResponse())->setJson($json); | 490 | return $this->sendResponse($entry); |
296 | } | 491 | } |
297 | 492 | ||
298 | /** | 493 | /** |
@@ -313,7 +508,7 @@ class EntryRestController extends WallabagRestController | |||
313 | $this->validateUserAccess($entry->getUser()->getId()); | 508 | $this->validateUserAccess($entry->getUser()->getId()); |
314 | 509 | ||
315 | try { | 510 | try { |
316 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | 511 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); |
317 | } catch (\Exception $e) { | 512 | } catch (\Exception $e) { |
318 | $this->get('logger')->error('Error while saving an entry', [ | 513 | $this->get('logger')->error('Error while saving an entry', [ |
319 | 'exception' => $e, | 514 | 'exception' => $e, |
@@ -335,9 +530,7 @@ class EntryRestController extends WallabagRestController | |||
335 | // entry saved, dispatch event about it! | 530 | // entry saved, dispatch event about it! |
336 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 531 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
337 | 532 | ||
338 | $json = $this->get('serializer')->serialize($entry, 'json'); | 533 | return $this->sendResponse($entry); |
339 | |||
340 | return (new JsonResponse())->setJson($json); | ||
341 | } | 534 | } |
342 | 535 | ||
343 | /** | 536 | /** |
@@ -363,9 +556,7 @@ class EntryRestController extends WallabagRestController | |||
363 | // entry deleted, dispatch event about it! | 556 | // entry deleted, dispatch event about it! |
364 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | 557 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
365 | 558 | ||
366 | $json = $this->get('serializer')->serialize($entry, 'json'); | 559 | return $this->sendResponse($entry); |
367 | |||
368 | return (new JsonResponse())->setJson($json); | ||
369 | } | 560 | } |
370 | 561 | ||
371 | /** | 562 | /** |
@@ -384,9 +575,7 @@ class EntryRestController extends WallabagRestController | |||
384 | $this->validateAuthentication(); | 575 | $this->validateAuthentication(); |
385 | $this->validateUserAccess($entry->getUser()->getId()); | 576 | $this->validateUserAccess($entry->getUser()->getId()); |
386 | 577 | ||
387 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | 578 | return $this->sendResponse($entry->getTags()); |
388 | |||
389 | return (new JsonResponse())->setJson($json); | ||
390 | } | 579 | } |
391 | 580 | ||
392 | /** | 581 | /** |
@@ -410,16 +599,14 @@ class EntryRestController extends WallabagRestController | |||
410 | 599 | ||
411 | $tags = $request->request->get('tags', ''); | 600 | $tags = $request->request->get('tags', ''); |
412 | if (!empty($tags)) { | 601 | if (!empty($tags)) { |
413 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 602 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); |
414 | } | 603 | } |
415 | 604 | ||
416 | $em = $this->getDoctrine()->getManager(); | 605 | $em = $this->getDoctrine()->getManager(); |
417 | $em->persist($entry); | 606 | $em->persist($entry); |
418 | $em->flush(); | 607 | $em->flush(); |
419 | 608 | ||
420 | $json = $this->get('serializer')->serialize($entry, 'json'); | 609 | return $this->sendResponse($entry); |
421 | |||
422 | return (new JsonResponse())->setJson($json); | ||
423 | } | 610 | } |
424 | 611 | ||
425 | /** | 612 | /** |
@@ -444,8 +631,170 @@ class EntryRestController extends WallabagRestController | |||
444 | $em->persist($entry); | 631 | $em->persist($entry); |
445 | $em->flush(); | 632 | $em->flush(); |
446 | 633 | ||
447 | $json = $this->get('serializer')->serialize($entry, 'json'); | 634 | return $this->sendResponse($entry); |
635 | } | ||
636 | |||
637 | /** | ||
638 | * Handles an entries list delete tags from them. | ||
639 | * | ||
640 | * @ApiDoc( | ||
641 | * parameters={ | ||
642 | * {"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."} | ||
643 | * } | ||
644 | * ) | ||
645 | * | ||
646 | * @return JsonResponse | ||
647 | */ | ||
648 | public function deleteEntriesTagsListAction(Request $request) | ||
649 | { | ||
650 | $this->validateAuthentication(); | ||
651 | |||
652 | $list = json_decode($request->query->get('list', [])); | ||
653 | |||
654 | if (empty($list)) { | ||
655 | return $this->sendResponse([]); | ||
656 | } | ||
657 | |||
658 | // handle multiple urls | ||
659 | $results = []; | ||
660 | |||
661 | foreach ($list as $key => $element) { | ||
662 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
663 | $element->url, | ||
664 | $this->getUser()->getId() | ||
665 | ); | ||
666 | |||
667 | $results[$key]['url'] = $element->url; | ||
668 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
669 | |||
670 | $tags = $element->tags; | ||
671 | |||
672 | if (false !== $entry && !(empty($tags))) { | ||
673 | $tags = explode(',', $tags); | ||
674 | foreach ($tags as $label) { | ||
675 | $label = trim($label); | ||
676 | |||
677 | $tag = $this->getDoctrine() | ||
678 | ->getRepository('WallabagCoreBundle:Tag') | ||
679 | ->findOneByLabel($label); | ||
680 | |||
681 | if (false !== $tag) { | ||
682 | $entry->removeTag($tag); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | $em = $this->getDoctrine()->getManager(); | ||
687 | $em->persist($entry); | ||
688 | $em->flush(); | ||
689 | } | ||
690 | } | ||
691 | |||
692 | return $this->sendResponse($results); | ||
693 | } | ||
694 | |||
695 | /** | ||
696 | * Handles an entries list and add tags to them. | ||
697 | * | ||
698 | * @ApiDoc( | ||
699 | * parameters={ | ||
700 | * {"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."} | ||
701 | * } | ||
702 | * ) | ||
703 | * | ||
704 | * @return JsonResponse | ||
705 | */ | ||
706 | public function postEntriesTagsListAction(Request $request) | ||
707 | { | ||
708 | $this->validateAuthentication(); | ||
709 | |||
710 | $list = json_decode($request->query->get('list', [])); | ||
711 | |||
712 | if (empty($list)) { | ||
713 | return $this->sendResponse([]); | ||
714 | } | ||
715 | |||
716 | $results = []; | ||
717 | |||
718 | // handle multiple urls | ||
719 | foreach ($list as $key => $element) { | ||
720 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
721 | $element->url, | ||
722 | $this->getUser()->getId() | ||
723 | ); | ||
724 | |||
725 | $results[$key]['url'] = $element->url; | ||
726 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
727 | |||
728 | $tags = $element->tags; | ||
729 | |||
730 | if (false !== $entry && !(empty($tags))) { | ||
731 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); | ||
732 | |||
733 | $em = $this->getDoctrine()->getManager(); | ||
734 | $em->persist($entry); | ||
735 | $em->flush(); | ||
736 | } | ||
737 | } | ||
738 | |||
739 | return $this->sendResponse($results); | ||
740 | } | ||
741 | |||
742 | /** | ||
743 | * Shortcut to send data serialized in json. | ||
744 | * | ||
745 | * @param mixed $data | ||
746 | * | ||
747 | * @return JsonResponse | ||
748 | */ | ||
749 | private function sendResponse($data) | ||
750 | { | ||
751 | // https://github.com/schmittjoh/JMSSerializerBundle/issues/293 | ||
752 | $context = new SerializationContext(); | ||
753 | $context->setSerializeNull(true); | ||
754 | |||
755 | $json = $this->get('jms_serializer')->serialize($data, 'json', $context); | ||
448 | 756 | ||
449 | return (new JsonResponse())->setJson($json); | 757 | return (new JsonResponse())->setJson($json); |
450 | } | 758 | } |
759 | |||
760 | /** | ||
761 | * Retrieve value from the request. | ||
762 | * Used for POST & PATCH on a an entry. | ||
763 | * | ||
764 | * @param Request $request | ||
765 | * | ||
766 | * @return array | ||
767 | */ | ||
768 | private function retrieveValueFromRequest(Request $request) | ||
769 | { | ||
770 | return [ | ||
771 | 'title' => $request->request->get('title'), | ||
772 | 'tags' => $request->request->get('tags', []), | ||
773 | 'isArchived' => $request->request->get('archive'), | ||
774 | 'isStarred' => $request->request->get('starred'), | ||
775 | 'isPublic' => $request->request->get('public'), | ||
776 | 'content' => $request->request->get('content'), | ||
777 | 'language' => $request->request->get('language'), | ||
778 | 'picture' => $request->request->get('preview_picture'), | ||
779 | 'publishedAt' => $request->request->get('published_at'), | ||
780 | 'authors' => $request->request->get('authors', ''), | ||
781 | ]; | ||
782 | } | ||
783 | |||
784 | /** | ||
785 | * Return information about the entry if it exist and depending on the id or not. | ||
786 | * | ||
787 | * @param Entry|null $entry | ||
788 | * @param bool $returnId | ||
789 | * | ||
790 | * @return bool|int | ||
791 | */ | ||
792 | private function returnExistInformation($entry, $returnId) | ||
793 | { | ||
794 | if ($returnId) { | ||
795 | return $entry instanceof Entry ? $entry->getId() : null; | ||
796 | } | ||
797 | |||
798 | return $entry instanceof Entry; | ||
799 | } | ||
451 | } | 800 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php index bc6d4e64..9d333fe4 100644 --- a/src/Wallabag/ApiBundle/Controller/TagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php | |||
@@ -3,8 +3,8 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
6 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Component\HttpFoundation\JsonResponse; | 6 | use Symfony\Component\HttpFoundation\JsonResponse; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
8 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Wallabag\CoreBundle\Entity\Entry; |
9 | use Wallabag\CoreBundle\Entity\Tag; | 9 | use Wallabag\CoreBundle\Entity\Tag; |
10 | 10 | ||
@@ -25,13 +25,13 @@ class TagRestController extends WallabagRestController | |||
25 | ->getRepository('WallabagCoreBundle:Tag') | 25 | ->getRepository('WallabagCoreBundle:Tag') |
26 | ->findAllTags($this->getUser()->getId()); | 26 | ->findAllTags($this->getUser()->getId()); |
27 | 27 | ||
28 | $json = $this->get('serializer')->serialize($tags, 'json'); | 28 | $json = $this->get('jms_serializer')->serialize($tags, 'json'); |
29 | 29 | ||
30 | return (new JsonResponse())->setJson($json); | 30 | return (new JsonResponse())->setJson($json); |
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 | ||
@@ -58,7 +58,7 @@ class TagRestController extends WallabagRestController | |||
58 | 58 | ||
59 | $this->cleanOrphanTag($tag); | 59 | $this->cleanOrphanTag($tag); |
60 | 60 | ||
61 | $json = $this->get('serializer')->serialize($tag, 'json'); | 61 | $json = $this->get('jms_serializer')->serialize($tag, 'json'); |
62 | 62 | ||
63 | return (new JsonResponse())->setJson($json); | 63 | return (new JsonResponse())->setJson($json); |
64 | } | 64 | } |
@@ -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 | ||
@@ -100,13 +100,13 @@ class TagRestController extends WallabagRestController | |||
100 | 100 | ||
101 | $this->cleanOrphanTag($tags); | 101 | $this->cleanOrphanTag($tags); |
102 | 102 | ||
103 | $json = $this->get('serializer')->serialize($tags, 'json'); | 103 | $json = $this->get('jms_serializer')->serialize($tags, 'json'); |
104 | 104 | ||
105 | return (new JsonResponse())->setJson($json); | 105 | return (new JsonResponse())->setJson($json); |
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={ |
@@ -126,7 +126,7 @@ class TagRestController extends WallabagRestController | |||
126 | 126 | ||
127 | $this->cleanOrphanTag($tag); | 127 | $this->cleanOrphanTag($tag); |
128 | 128 | ||
129 | $json = $this->get('serializer')->serialize($tag, 'json'); | 129 | $json = $this->get('jms_serializer')->serialize($tag, 'json'); |
130 | 130 | ||
131 | return (new JsonResponse())->setJson($json); | 131 | return (new JsonResponse())->setJson($json); |
132 | } | 132 | } |
@@ -145,7 +145,7 @@ class TagRestController extends WallabagRestController | |||
145 | $em = $this->getDoctrine()->getManager(); | 145 | $em = $this->getDoctrine()->getManager(); |
146 | 146 | ||
147 | foreach ($tags as $tag) { | 147 | foreach ($tags as $tag) { |
148 | if (count($tag->getEntries()) === 0) { | 148 | if (0 === count($tag->getEntries())) { |
149 | $em->remove($tag); | 149 | $em->remove($tag); |
150 | } | 150 | } |
151 | } | 151 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php new file mode 100644 index 00000000..a1378fc5 --- /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\JsonResponse; | ||
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Wallabag\ApiBundle\Entity\Client; | ||
12 | use Wallabag\UserBundle\Entity\User; | ||
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('jms_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('jms_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('jms_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/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index b1e08ca4..7d8cfbba 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php | |||
@@ -19,7 +19,7 @@ class WallabagRestController extends FOSRestController | |||
19 | public function getVersionAction() | 19 | public function getVersionAction() |
20 | { | 20 | { |
21 | $version = $this->container->getParameter('wallabag_core.version'); | 21 | $version = $this->container->getParameter('wallabag_core.version'); |
22 | $json = $this->get('serializer')->serialize($version, 'json'); | 22 | $json = $this->get('jms_serializer')->serialize($version, 'json'); |
23 | 23 | ||
24 | return (new JsonResponse())->setJson($json); | 24 | return (new JsonResponse())->setJson($json); |
25 | } | 25 | } |
@@ -40,8 +40,8 @@ class WallabagRestController extends FOSRestController | |||
40 | protected function validateUserAccess($requestUserId) | 40 | protected function validateUserAccess($requestUserId) |
41 | { | 41 | { |
42 | $user = $this->get('security.token_storage')->getToken()->getUser(); | 42 | $user = $this->get('security.token_storage')->getToken()->getUser(); |
43 | if ($requestUserId != $user->getId()) { | 43 | if ($requestUserId !== $user->getId()) { |
44 | throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId()); | 44 | throw $this->createAccessDeniedException('Access forbidden. Entry user id: ' . $requestUserId . ', logged user id: ' . $user->getId()); |
45 | } | 45 | } |
46 | } | 46 | } |
47 | } | 47 | } |
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php index 9ed9f980..e6f98f98 100644 --- a/src/Wallabag/ApiBundle/Entity/Client.php +++ b/src/Wallabag/ApiBundle/Entity/Client.php | |||
@@ -4,6 +4,9 @@ namespace Wallabag\ApiBundle\Entity; | |||
4 | 4 | ||
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 JMS\Serializer\Annotation\Groups; | ||
8 | use JMS\Serializer\Annotation\SerializedName; | ||
9 | use JMS\Serializer\Annotation\VirtualProperty; | ||
7 | use Wallabag\UserBundle\Entity\User; | 10 | use Wallabag\UserBundle\Entity\User; |
8 | 11 | ||
9 | /** | 12 | /** |
@@ -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/Form/Type/ClientType.php b/src/Wallabag/ApiBundle/Form/Type/ClientType.php index eaea4feb..fc22538f 100644 --- a/src/Wallabag/ApiBundle/Form/Type/ClientType.php +++ b/src/Wallabag/ApiBundle/Form/Type/ClientType.php | |||
@@ -5,8 +5,8 @@ namespace Wallabag\ApiBundle\Form\Type; | |||
5 | use Symfony\Component\Form\AbstractType; | 5 | use Symfony\Component\Form\AbstractType; |
6 | use Symfony\Component\Form\CallbackTransformer; | 6 | use Symfony\Component\Form\CallbackTransformer; |
7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | 7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; |
8 | use Symfony\Component\Form\Extension\Core\Type\UrlType; | ||
9 | use Symfony\Component\Form\Extension\Core\Type\TextType; | 8 | use Symfony\Component\Form\Extension\Core\Type\TextType; |
9 | use Symfony\Component\Form\Extension\Core\Type\UrlType; | ||
10 | use Symfony\Component\Form\FormBuilderInterface; | 10 | use Symfony\Component\Form\FormBuilderInterface; |
11 | use Symfony\Component\OptionsResolver\OptionsResolver; | 11 | use Symfony\Component\OptionsResolver\OptionsResolver; |
12 | 12 | ||
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..b58909db --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php | |||
@@ -0,0 +1,117 @@ | |||
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 Symfony\Component\Console\Style\SymfonyStyle; | ||
11 | use Wallabag\CoreBundle\Entity\Entry; | ||
12 | use Wallabag\UserBundle\Entity\User; | ||
13 | |||
14 | class CleanDuplicatesCommand extends ContainerAwareCommand | ||
15 | { | ||
16 | /** @var SymfonyStyle */ | ||
17 | protected $io; | ||
18 | |||
19 | protected $duplicates = 0; | ||
20 | |||
21 | protected function configure() | ||
22 | { | ||
23 | $this | ||
24 | ->setName('wallabag:clean-duplicates') | ||
25 | ->setDescription('Cleans the database for duplicates') | ||
26 | ->setHelp('This command helps you to clean your articles list in case of duplicates') | ||
27 | ->addArgument( | ||
28 | 'username', | ||
29 | InputArgument::OPTIONAL, | ||
30 | 'User to clean' | ||
31 | ); | ||
32 | } | ||
33 | |||
34 | protected function execute(InputInterface $input, OutputInterface $output) | ||
35 | { | ||
36 | $this->io = new SymfonyStyle($input, $output); | ||
37 | |||
38 | $username = $input->getArgument('username'); | ||
39 | |||
40 | if ($username) { | ||
41 | try { | ||
42 | $user = $this->getUser($username); | ||
43 | $this->cleanDuplicates($user); | ||
44 | } catch (NoResultException $e) { | ||
45 | $this->io->error(sprintf('User "%s" not found.', $username)); | ||
46 | |||
47 | return 1; | ||
48 | } | ||
49 | |||
50 | $this->io->success('Finished cleaning.'); | ||
51 | } else { | ||
52 | $users = $this->getContainer()->get('wallabag_user.user_repository')->findAll(); | ||
53 | |||
54 | $this->io->text(sprintf('Cleaning through <info>%d</info> user accounts', count($users))); | ||
55 | |||
56 | foreach ($users as $user) { | ||
57 | $this->io->text(sprintf('Processing user <info>%s</info>', $user->getUsername())); | ||
58 | $this->cleanDuplicates($user); | ||
59 | } | ||
60 | $this->io->success(sprintf('Finished cleaning. %d duplicates found in total', $this->duplicates)); | ||
61 | } | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * @param User $user | ||
68 | */ | ||
69 | private function cleanDuplicates(User $user) | ||
70 | { | ||
71 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | ||
72 | $repo = $this->getContainer()->get('wallabag_core.entry_repository'); | ||
73 | |||
74 | $entries = $repo->findAllEntriesIdAndUrlByUserId($user->getId()); | ||
75 | |||
76 | $duplicatesCount = 0; | ||
77 | $urls = []; | ||
78 | foreach ($entries as $entry) { | ||
79 | $url = $this->similarUrl($entry['url']); | ||
80 | |||
81 | /* @var $entry Entry */ | ||
82 | if (in_array($url, $urls, true)) { | ||
83 | ++$duplicatesCount; | ||
84 | |||
85 | $em->remove($repo->find($entry['id'])); | ||
86 | $em->flush(); // Flushing at the end of the loop would require the instance not being online | ||
87 | } else { | ||
88 | $urls[] = $entry['url']; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | $this->duplicates += $duplicatesCount; | ||
93 | |||
94 | $this->io->text(sprintf('Cleaned <info>%d</info> duplicates for user <info>%s</info>', $duplicatesCount, $user->getUserName())); | ||
95 | } | ||
96 | |||
97 | private function similarUrl($url) | ||
98 | { | ||
99 | if (in_array(substr($url, -1), ['/', '#'], true)) { // get rid of "/" and "#" and the end of urls | ||
100 | return substr($url, 0, strlen($url)); | ||
101 | } | ||
102 | |||
103 | return $url; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Fetches a user from its username. | ||
108 | * | ||
109 | * @param string $username | ||
110 | * | ||
111 | * @return \Wallabag\UserBundle\Entity\User | ||
112 | */ | ||
113 | private function getUser($username) | ||
114 | { | ||
115 | return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); | ||
116 | } | ||
117 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/ExportCommand.php b/src/Wallabag/CoreBundle/Command/ExportCommand.php index e3d3b399..75e9ad91 100644 --- a/src/Wallabag/CoreBundle/Command/ExportCommand.php +++ b/src/Wallabag/CoreBundle/Command/ExportCommand.php | |||
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | |||
7 | use Symfony\Component\Console\Input\InputArgument; | 7 | use Symfony\Component\Console\Input\InputArgument; |
8 | use Symfony\Component\Console\Input\InputInterface; | 8 | use Symfony\Component\Console\Input\InputInterface; |
9 | use Symfony\Component\Console\Output\OutputInterface; | 9 | use Symfony\Component\Console\Output\OutputInterface; |
10 | use Symfony\Component\Console\Style\SymfonyStyle; | ||
10 | 11 | ||
11 | class ExportCommand extends ContainerAwareCommand | 12 | class ExportCommand extends ContainerAwareCommand |
12 | { | 13 | { |
@@ -31,47 +32,44 @@ class ExportCommand extends ContainerAwareCommand | |||
31 | 32 | ||
32 | protected function execute(InputInterface $input, OutputInterface $output) | 33 | protected function execute(InputInterface $input, OutputInterface $output) |
33 | { | 34 | { |
35 | $io = new SymfonyStyle($input, $output); | ||
36 | |||
34 | try { | 37 | try { |
35 | $user = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($input->getArgument('username')); | 38 | $user = $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($input->getArgument('username')); |
36 | } catch (NoResultException $e) { | 39 | } catch (NoResultException $e) { |
37 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); | 40 | $io->error(sprintf('User "%s" not found.', $input->getArgument('username'))); |
38 | 41 | ||
39 | return 1; | 42 | return 1; |
40 | } | 43 | } |
41 | 44 | ||
42 | $entries = $this->getDoctrine() | 45 | $entries = $this->getContainer()->get('wallabag_core.entry_repository') |
43 | ->getRepository('WallabagCoreBundle:Entry') | ||
44 | ->getBuilderForAllByUser($user->getId()) | 46 | ->getBuilderForAllByUser($user->getId()) |
45 | ->getQuery() | 47 | ->getQuery() |
46 | ->getResult(); | 48 | ->getResult(); |
47 | 49 | ||
48 | $output->write(sprintf('Exporting %d entrie(s) for user « <comment>%s</comment> »... ', count($entries), $user->getUserName())); | 50 | $io->text(sprintf('Exporting <info>%d</info> entrie(s) for user <info>%s</info>...', count($entries), $user->getUserName())); |
49 | 51 | ||
50 | $filePath = $input->getArgument('filepath'); | 52 | $filePath = $input->getArgument('filepath'); |
51 | 53 | ||
52 | if (!$filePath) { | 54 | if (!$filePath) { |
53 | $filePath = $this->getContainer()->getParameter('kernel.root_dir').'/../'.sprintf('%s-export.json', $user->getUsername()); | 55 | $filePath = $this->getContainer()->getParameter('kernel.project_dir') . '/' . sprintf('%s-export.json', $user->getUsername()); |
54 | } | 56 | } |
55 | 57 | ||
56 | try { | 58 | try { |
57 | $data = $this->getContainer()->get('wallabag_core.helper.entries_export') | 59 | $data = $this->getContainer()->get('wallabag_core.helper.entries_export') |
58 | ->setEntries($entries) | 60 | ->setEntries($entries) |
59 | ->updateTitle('All') | 61 | ->updateTitle('All') |
62 | ->updateAuthor('All') | ||
60 | ->exportJsonData(); | 63 | ->exportJsonData(); |
61 | file_put_contents($filePath, $data); | 64 | file_put_contents($filePath, $data); |
62 | } catch (\InvalidArgumentException $e) { | 65 | } catch (\InvalidArgumentException $e) { |
63 | $output->writeln(sprintf('<error>Error: "%s"</error>', $e->getMessage())); | 66 | $io->error(sprintf('Error: "%s"', $e->getMessage())); |
64 | 67 | ||
65 | return 1; | 68 | return 1; |
66 | } | 69 | } |
67 | 70 | ||
68 | $output->writeln('<info>Done.</info>'); | 71 | $io->success('Done.'); |
69 | 72 | ||
70 | return 0; | 73 | return 0; |
71 | } | 74 | } |
72 | |||
73 | private function getDoctrine() | ||
74 | { | ||
75 | return $this->getContainer()->get('doctrine'); | ||
76 | } | ||
77 | } | 75 | } |
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php index f0738b91..877dbfa2 100644 --- a/src/Wallabag/CoreBundle/Command/InstallCommand.php +++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php | |||
@@ -2,19 +2,17 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Command; | 3 | namespace Wallabag\CoreBundle\Command; |
4 | 4 | ||
5 | use Craue\ConfigBundle\Entity\Setting; | ||
5 | use FOS\UserBundle\Event\UserEvent; | 6 | use FOS\UserBundle\Event\UserEvent; |
6 | use FOS\UserBundle\FOSUserEvents; | 7 | use FOS\UserBundle\FOSUserEvents; |
7 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | 8 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; |
8 | use Symfony\Component\Console\Helper\Table; | ||
9 | use Symfony\Component\Console\Input\ArrayInput; | 9 | use Symfony\Component\Console\Input\ArrayInput; |
10 | use Symfony\Component\Console\Input\InputInterface; | 10 | use Symfony\Component\Console\Input\InputInterface; |
11 | use Symfony\Component\Console\Input\InputOption; | 11 | use Symfony\Component\Console\Input\InputOption; |
12 | use Symfony\Component\Console\Output\BufferedOutput; | 12 | use Symfony\Component\Console\Output\BufferedOutput; |
13 | use Symfony\Component\Console\Output\OutputInterface; | 13 | use Symfony\Component\Console\Output\OutputInterface; |
14 | use Symfony\Component\Console\Question\ConfirmationQuestion; | ||
15 | use Symfony\Component\Console\Question\Question; | 14 | use Symfony\Component\Console\Question\Question; |
16 | use Wallabag\CoreBundle\Entity\Config; | 15 | use Symfony\Component\Console\Style\SymfonyStyle; |
17 | use Craue\ConfigBundle\Entity\Setting; | ||
18 | 16 | ||
19 | class InstallCommand extends ContainerAwareCommand | 17 | class InstallCommand extends ContainerAwareCommand |
20 | { | 18 | { |
@@ -24,9 +22,9 @@ class InstallCommand extends ContainerAwareCommand | |||
24 | protected $defaultInput; | 22 | protected $defaultInput; |
25 | 23 | ||
26 | /** | 24 | /** |
27 | * @var OutputInterface | 25 | * @var SymfonyStyle |
28 | */ | 26 | */ |
29 | protected $defaultOutput; | 27 | protected $io; |
30 | 28 | ||
31 | /** | 29 | /** |
32 | * @var array | 30 | * @var array |
@@ -53,25 +51,27 @@ class InstallCommand extends ContainerAwareCommand | |||
53 | protected function execute(InputInterface $input, OutputInterface $output) | 51 | protected function execute(InputInterface $input, OutputInterface $output) |
54 | { | 52 | { |
55 | $this->defaultInput = $input; | 53 | $this->defaultInput = $input; |
56 | $this->defaultOutput = $output; | ||
57 | 54 | ||
58 | $output->writeln('<info>Installing wallabag...</info>'); | 55 | $this->io = new SymfonyStyle($input, $output); |
59 | $output->writeln(''); | 56 | |
57 | $this->io->title('Wallabag installer'); | ||
60 | 58 | ||
61 | $this | 59 | $this |
62 | ->checkRequirements() | 60 | ->checkRequirements() |
63 | ->setupDatabase() | 61 | ->setupDatabase() |
64 | ->setupAdmin() | 62 | ->setupAdmin() |
65 | ->setupConfig() | 63 | ->setupConfig() |
64 | ->runMigrations() | ||
66 | ; | 65 | ; |
67 | 66 | ||
68 | $output->writeln('<info>wallabag has been successfully installed.</info>'); | 67 | $this->io->success('Wallabag has been successfully installed.'); |
69 | $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>'); | 68 | $this->io->note('Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000'); |
70 | } | 69 | } |
71 | 70 | ||
72 | protected function checkRequirements() | 71 | protected function checkRequirements() |
73 | { | 72 | { |
74 | $this->defaultOutput->writeln('<info><comment>Step 1 of 4.</comment> Checking system requirements.</info>'); | 73 | $this->io->section('Step 1 of 5: Checking system requirements.'); |
74 | |||
75 | $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); | 75 | $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); |
76 | 76 | ||
77 | $rows = []; | 77 | $rows = []; |
@@ -85,7 +85,7 @@ class InstallCommand extends ContainerAwareCommand | |||
85 | if (!extension_loaded($this->getContainer()->getParameter('database_driver'))) { | 85 | if (!extension_loaded($this->getContainer()->getParameter('database_driver'))) { |
86 | $fulfilled = false; | 86 | $fulfilled = false; |
87 | $status = '<error>ERROR!</error>'; | 87 | $status = '<error>ERROR!</error>'; |
88 | $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; | 88 | $help = 'Database driver "' . $this->getContainer()->getParameter('database_driver') . '" is not installed.'; |
89 | } | 89 | } |
90 | 90 | ||
91 | $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help]; | 91 | $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help]; |
@@ -100,10 +100,10 @@ class InstallCommand extends ContainerAwareCommand | |||
100 | $conn->connect(); | 100 | $conn->connect(); |
101 | } catch (\Exception $e) { | 101 | } catch (\Exception $e) { |
102 | if (false === strpos($e->getMessage(), 'Unknown database') | 102 | if (false === strpos($e->getMessage(), 'Unknown database') |
103 | && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { | 103 | && false === strpos($e->getMessage(), 'database "' . $this->getContainer()->getParameter('database_name') . '" does not exist')) { |
104 | $fulfilled = false; | 104 | $fulfilled = false; |
105 | $status = '<error>ERROR!</error>'; | 105 | $status = '<error>ERROR!</error>'; |
106 | $help = 'Can\'t connect to the database: '.$e->getMessage(); | 106 | $help = 'Can\'t connect to the database: ' . $e->getMessage(); |
107 | } | 107 | } |
108 | } | 108 | } |
109 | 109 | ||
@@ -122,7 +122,7 @@ class InstallCommand extends ContainerAwareCommand | |||
122 | if (false === version_compare($version, $minimalVersion, '>')) { | 122 | if (false === version_compare($version, $minimalVersion, '>')) { |
123 | $fulfilled = false; | 123 | $fulfilled = false; |
124 | $status = '<error>ERROR!</error>'; | 124 | $status = '<error>ERROR!</error>'; |
125 | $help = 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).'; | 125 | $help = 'Your MySQL version (' . $version . ') is too old, consider upgrading (' . $minimalVersion . '+).'; |
126 | } | 126 | } |
127 | } | 127 | } |
128 | 128 | ||
@@ -136,50 +136,44 @@ class InstallCommand extends ContainerAwareCommand | |||
136 | if (isset($matches[1]) & version_compare($matches[1], '9.2.0', '<')) { | 136 | if (isset($matches[1]) & version_compare($matches[1], '9.2.0', '<')) { |
137 | $fulfilled = false; | 137 | $fulfilled = false; |
138 | $status = '<error>ERROR!</error>'; | 138 | $status = '<error>ERROR!</error>'; |
139 | $help = 'PostgreSQL should be greater than 9.1 (actual version: '.$matches[1].')'; | 139 | $help = 'PostgreSQL should be greater than 9.1 (actual version: ' . $matches[1] . ')'; |
140 | } | 140 | } |
141 | } | 141 | } |
142 | 142 | ||
143 | $rows[] = [$label, $status, $help]; | 143 | $rows[] = [$label, $status, $help]; |
144 | 144 | ||
145 | foreach ($this->functionExists as $functionRequired) { | 145 | foreach ($this->functionExists as $functionRequired) { |
146 | $label = '<comment>'.$functionRequired.'</comment>'; | 146 | $label = '<comment>' . $functionRequired . '</comment>'; |
147 | $status = '<info>OK!</info>'; | 147 | $status = '<info>OK!</info>'; |
148 | $help = ''; | 148 | $help = ''; |
149 | 149 | ||
150 | if (!function_exists($functionRequired)) { | 150 | if (!function_exists($functionRequired)) { |
151 | $fulfilled = false; | 151 | $fulfilled = false; |
152 | $status = '<error>ERROR!</error>'; | 152 | $status = '<error>ERROR!</error>'; |
153 | $help = 'You need the '.$functionRequired.' function activated'; | 153 | $help = 'You need the ' . $functionRequired . ' function activated'; |
154 | } | 154 | } |
155 | 155 | ||
156 | $rows[] = [$label, $status, $help]; | 156 | $rows[] = [$label, $status, $help]; |
157 | } | 157 | } |
158 | 158 | ||
159 | $table = new Table($this->defaultOutput); | 159 | $this->io->table(['Checked', 'Status', 'Recommendation'], $rows); |
160 | $table | ||
161 | ->setHeaders(['Checked', 'Status', 'Recommendation']) | ||
162 | ->setRows($rows) | ||
163 | ->render(); | ||
164 | 160 | ||
165 | if (!$fulfilled) { | 161 | if (!$fulfilled) { |
166 | throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); | 162 | throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); |
167 | } | 163 | } |
168 | 164 | ||
169 | $this->defaultOutput->writeln('<info>Success! Your system can run wallabag properly.</info>'); | 165 | $this->io->success('Success! Your system can run wallabag properly.'); |
170 | |||
171 | $this->defaultOutput->writeln(''); | ||
172 | 166 | ||
173 | return $this; | 167 | return $this; |
174 | } | 168 | } |
175 | 169 | ||
176 | protected function setupDatabase() | 170 | protected function setupDatabase() |
177 | { | 171 | { |
178 | $this->defaultOutput->writeln('<info><comment>Step 2 of 4.</comment> Setting up database.</info>'); | 172 | $this->io->section('Step 2 of 5: Setting up database.'); |
179 | 173 | ||
180 | // user want to reset everything? Don't care about what is already here | 174 | // user want to reset everything? Don't care about what is already here |
181 | if (true === $this->defaultInput->getOption('reset')) { | 175 | if (true === $this->defaultInput->getOption('reset')) { |
182 | $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache'); | 176 | $this->io->text('Dropping database, creating database and schema, clearing the cache'); |
183 | 177 | ||
184 | $this | 178 | $this |
185 | ->runCommand('doctrine:database:drop', ['--force' => true]) | 179 | ->runCommand('doctrine:database:drop', ['--force' => true]) |
@@ -188,13 +182,13 @@ class InstallCommand extends ContainerAwareCommand | |||
188 | ->runCommand('cache:clear') | 182 | ->runCommand('cache:clear') |
189 | ; | 183 | ; |
190 | 184 | ||
191 | $this->defaultOutput->writeln(''); | 185 | $this->io->newLine(); |
192 | 186 | ||
193 | return $this; | 187 | return $this; |
194 | } | 188 | } |
195 | 189 | ||
196 | if (!$this->isDatabasePresent()) { | 190 | if (!$this->isDatabasePresent()) { |
197 | $this->defaultOutput->writeln('Creating database and schema, clearing the cache'); | 191 | $this->io->text('Creating database and schema, clearing the cache'); |
198 | 192 | ||
199 | $this | 193 | $this |
200 | ->runCommand('doctrine:database:create') | 194 | ->runCommand('doctrine:database:create') |
@@ -202,16 +196,13 @@ class InstallCommand extends ContainerAwareCommand | |||
202 | ->runCommand('cache:clear') | 196 | ->runCommand('cache:clear') |
203 | ; | 197 | ; |
204 | 198 | ||
205 | $this->defaultOutput->writeln(''); | 199 | $this->io->newLine(); |
206 | 200 | ||
207 | return $this; | 201 | return $this; |
208 | } | 202 | } |
209 | 203 | ||
210 | $questionHelper = $this->getHelper('question'); | 204 | if ($this->io->confirm('It appears that your database already exists. Would you like to reset it?', false)) { |
211 | $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); | 205 | $this->io->text('Dropping database, creating database and schema...'); |
212 | |||
213 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | ||
214 | $this->defaultOutput->writeln('Droping database, creating database and schema'); | ||
215 | 206 | ||
216 | $this | 207 | $this |
217 | ->runCommand('doctrine:database:drop', ['--force' => true]) | 208 | ->runCommand('doctrine:database:drop', ['--force' => true]) |
@@ -219,9 +210,8 @@ class InstallCommand extends ContainerAwareCommand | |||
219 | ->runCommand('doctrine:schema:create') | 210 | ->runCommand('doctrine:schema:create') |
220 | ; | 211 | ; |
221 | } elseif ($this->isSchemaPresent()) { | 212 | } elseif ($this->isSchemaPresent()) { |
222 | $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); | 213 | if ($this->io->confirm('Seems like your database contains schema. Do you want to reset it?', false)) { |
223 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | 214 | $this->io->text('Dropping schema and creating schema...'); |
224 | $this->defaultOutput->writeln('Droping schema and creating schema'); | ||
225 | 215 | ||
226 | $this | 216 | $this |
227 | ->runCommand('doctrine:schema:drop', ['--force' => true]) | 217 | ->runCommand('doctrine:schema:drop', ['--force' => true]) |
@@ -229,29 +219,27 @@ class InstallCommand extends ContainerAwareCommand | |||
229 | ; | 219 | ; |
230 | } | 220 | } |
231 | } else { | 221 | } else { |
232 | $this->defaultOutput->writeln('Creating schema'); | 222 | $this->io->text('Creating schema...'); |
233 | 223 | ||
234 | $this | 224 | $this |
235 | ->runCommand('doctrine:schema:create') | 225 | ->runCommand('doctrine:schema:create') |
236 | ; | 226 | ; |
237 | } | 227 | } |
238 | 228 | ||
239 | $this->defaultOutput->writeln('Clearing the cache'); | 229 | $this->io->text('Clearing the cache...'); |
240 | $this->runCommand('cache:clear'); | 230 | $this->runCommand('cache:clear'); |
241 | 231 | ||
242 | $this->defaultOutput->writeln(''); | 232 | $this->io->newLine(); |
233 | $this->io->text('<info>Database successfully setup.</info>'); | ||
243 | 234 | ||
244 | return $this; | 235 | return $this; |
245 | } | 236 | } |
246 | 237 | ||
247 | protected function setupAdmin() | 238 | protected function setupAdmin() |
248 | { | 239 | { |
249 | $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>'); | 240 | $this->io->section('Step 3 of 5: Administration setup.'); |
250 | |||
251 | $questionHelper = $this->getHelperSet()->get('question'); | ||
252 | $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); | ||
253 | 241 | ||
254 | if (!$questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | 242 | if (!$this->io->confirm('Would you like to create a new admin user (recommended)?', true)) { |
255 | return $this; | 243 | return $this; |
256 | } | 244 | } |
257 | 245 | ||
@@ -260,14 +248,13 @@ class InstallCommand extends ContainerAwareCommand | |||
260 | $userManager = $this->getContainer()->get('fos_user.user_manager'); | 248 | $userManager = $this->getContainer()->get('fos_user.user_manager'); |
261 | $user = $userManager->createUser(); | 249 | $user = $userManager->createUser(); |
262 | 250 | ||
263 | $question = new Question('Username (default: wallabag) :', 'wallabag'); | 251 | $user->setUsername($this->io->ask('Username', 'wallabag')); |
264 | $user->setUsername($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); | ||
265 | 252 | ||
266 | $question = new Question('Password (default: wallabag) :', 'wallabag'); | 253 | $question = new Question('Password', 'wallabag'); |
267 | $user->setPlainPassword($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); | 254 | $question->setHidden(true); |
255 | $user->setPlainPassword($this->io->askQuestion($question)); | ||
268 | 256 | ||
269 | $question = new Question('Email:', ''); | 257 | $user->setEmail($this->io->ask('Email', '')); |
270 | $user->setEmail($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); | ||
271 | 258 | ||
272 | $user->setEnabled(true); | 259 | $user->setEnabled(true); |
273 | $user->addRole('ROLE_SUPER_ADMIN'); | 260 | $user->addRole('ROLE_SUPER_ADMIN'); |
@@ -278,168 +265,20 @@ class InstallCommand extends ContainerAwareCommand | |||
278 | $event = new UserEvent($user); | 265 | $event = new UserEvent($user); |
279 | $this->getContainer()->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); | 266 | $this->getContainer()->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); |
280 | 267 | ||
281 | $this->defaultOutput->writeln(''); | 268 | $this->io->text('<info>Administration successfully setup.</info>'); |
282 | 269 | ||
283 | return $this; | 270 | return $this; |
284 | } | 271 | } |
285 | 272 | ||
286 | protected function setupConfig() | 273 | protected function setupConfig() |
287 | { | 274 | { |
288 | $this->defaultOutput->writeln('<info><comment>Step 4 of 4.</comment> Config setup.</info>'); | 275 | $this->io->section('Step 4 of 5: Config setup.'); |
289 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | 276 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); |
290 | 277 | ||
291 | // cleanup before insert new stuff | 278 | // cleanup before insert new stuff |
292 | $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); | 279 | $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); |
293 | 280 | ||
294 | $settings = [ | 281 | 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(); | 282 | $newSetting = new Setting(); |
444 | $newSetting->setName($setting['name']); | 283 | $newSetting->setName($setting['name']); |
445 | $newSetting->setValue($setting['value']); | 284 | $newSetting->setValue($setting['value']); |
@@ -449,7 +288,19 @@ class InstallCommand extends ContainerAwareCommand | |||
449 | 288 | ||
450 | $em->flush(); | 289 | $em->flush(); |
451 | 290 | ||
452 | $this->defaultOutput->writeln(''); | 291 | $this->io->text('<info>Config successfully setup.</info>'); |
292 | |||
293 | return $this; | ||
294 | } | ||
295 | |||
296 | protected function runMigrations() | ||
297 | { | ||
298 | $this->io->section('Step 5 of 5: Run migrations.'); | ||
299 | |||
300 | $this | ||
301 | ->runCommand('doctrine:migrations:migrate', ['--no-interaction' => true]); | ||
302 | |||
303 | $this->io->text('<info>Migrations successfully executed.</info>'); | ||
453 | 304 | ||
454 | return $this; | 305 | return $this; |
455 | } | 306 | } |
@@ -480,20 +331,18 @@ class InstallCommand extends ContainerAwareCommand | |||
480 | $output = new BufferedOutput(); | 331 | $output = new BufferedOutput(); |
481 | $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); | 332 | $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); |
482 | 333 | ||
334 | // PDO does not always close the connection after Doctrine commands. | ||
335 | // See https://github.com/symfony/symfony/issues/11750. | ||
336 | $this->getContainer()->get('doctrine')->getManager()->getConnection()->close(); | ||
337 | |||
483 | if (0 !== $exitCode) { | 338 | if (0 !== $exitCode) { |
484 | $this->getApplication()->setAutoExit(true); | 339 | $this->getApplication()->setAutoExit(true); |
485 | 340 | ||
486 | $this->defaultOutput->writeln(''); | 341 | throw new \RuntimeException( |
487 | $this->defaultOutput->writeln('<error>The command "'.$command.'" generates some errors: </error>'); | 342 | 'The command "' . $command . "\" generates some errors: \n\n" |
488 | $this->defaultOutput->writeln($output->fetch()); | 343 | . $output->fetch()); |
489 | |||
490 | die(); | ||
491 | } | 344 | } |
492 | 345 | ||
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; | 346 | return $this; |
498 | } | 347 | } |
499 | 348 | ||
@@ -535,7 +384,7 @@ class InstallCommand extends ContainerAwareCommand | |||
535 | } | 384 | } |
536 | 385 | ||
537 | try { | 386 | try { |
538 | return in_array($databaseName, $schemaManager->listDatabases()); | 387 | return in_array($databaseName, $schemaManager->listDatabases(), true); |
539 | } catch (\Doctrine\DBAL\Exception\DriverException $e) { | 388 | } catch (\Doctrine\DBAL\Exception\DriverException $e) { |
540 | // it means we weren't able to get database list, assume the database doesn't exist | 389 | // it means we weren't able to get database list, assume the database doesn't exist |
541 | 390 | ||
diff --git a/src/Wallabag/CoreBundle/Command/ListUserCommand.php b/src/Wallabag/CoreBundle/Command/ListUserCommand.php new file mode 100644 index 00000000..68e515da --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/ListUserCommand.php | |||
@@ -0,0 +1,61 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
6 | use Symfony\Component\Console\Input\InputArgument; | ||
7 | use Symfony\Component\Console\Input\InputInterface; | ||
8 | use Symfony\Component\Console\Input\InputOption; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | use Symfony\Component\Console\Style\SymfonyStyle; | ||
11 | |||
12 | class ListUserCommand extends ContainerAwareCommand | ||
13 | { | ||
14 | protected function configure() | ||
15 | { | ||
16 | $this | ||
17 | ->setName('wallabag:user:list') | ||
18 | ->setDescription('List all users') | ||
19 | ->setHelp('This command list all existing users') | ||
20 | ->addArgument('search', InputArgument::OPTIONAL, 'Filter list by given search term') | ||
21 | ->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Max number of displayed users', 100) | ||
22 | ; | ||
23 | } | ||
24 | |||
25 | protected function execute(InputInterface $input, OutputInterface $output) | ||
26 | { | ||
27 | $io = new SymfonyStyle($input, $output); | ||
28 | |||
29 | $users = $this->getContainer()->get('wallabag_user.user_repository') | ||
30 | ->getQueryBuilderForSearch($input->getArgument('search')) | ||
31 | ->setMaxResults($input->getOption('limit')) | ||
32 | ->getQuery() | ||
33 | ->getResult(); | ||
34 | |||
35 | $nbUsers = $this->getContainer()->get('wallabag_user.user_repository') | ||
36 | ->getSumUsers(); | ||
37 | |||
38 | $rows = []; | ||
39 | foreach ($users as $user) { | ||
40 | $rows[] = [ | ||
41 | $user->getUsername(), | ||
42 | $user->getEmail(), | ||
43 | $user->isEnabled() ? 'yes' : 'no', | ||
44 | $user->hasRole('ROLE_SUPER_ADMIN') || $user->hasRole('ROLE_ADMIN') ? 'yes' : 'no', | ||
45 | ]; | ||
46 | } | ||
47 | |||
48 | $io->table(['username', 'email', 'is enabled?', 'is admin?'], $rows); | ||
49 | |||
50 | $io->success( | ||
51 | sprintf( | ||
52 | '%s/%s%s user(s) displayed.', | ||
53 | count($users), | ||
54 | $nbUsers, | ||
55 | null === $input->getArgument('search') ? '' : ' (filtered)' | ||
56 | ) | ||
57 | ); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php b/src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php new file mode 100644 index 00000000..91998841 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php | |||
@@ -0,0 +1,90 @@ | |||
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 Symfony\Component\Console\Style\SymfonyStyle; | ||
11 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
12 | |||
13 | class ReloadEntryCommand extends ContainerAwareCommand | ||
14 | { | ||
15 | protected function configure() | ||
16 | { | ||
17 | $this | ||
18 | ->setName('wallabag:entry:reload') | ||
19 | ->setDescription('Reload entries') | ||
20 | ->setHelp('This command reload entries') | ||
21 | ->addArgument('username', InputArgument::OPTIONAL, 'Reload entries only for the given user') | ||
22 | ; | ||
23 | } | ||
24 | |||
25 | protected function execute(InputInterface $input, OutputInterface $output) | ||
26 | { | ||
27 | $io = new SymfonyStyle($input, $output); | ||
28 | |||
29 | $userId = null; | ||
30 | if ($username = $input->getArgument('username')) { | ||
31 | try { | ||
32 | $userId = $this->getContainer() | ||
33 | ->get('wallabag_user.user_repository') | ||
34 | ->findOneByUserName($username) | ||
35 | ->getId(); | ||
36 | } catch (NoResultException $e) { | ||
37 | $io->error(sprintf('User "%s" not found.', $username)); | ||
38 | |||
39 | return 1; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | $entryRepository = $this->getContainer()->get('wallabag_core.entry_repository'); | ||
44 | $entryIds = $entryRepository->findAllEntriesIdByUserId($userId); | ||
45 | |||
46 | $nbEntries = count($entryIds); | ||
47 | if (!$nbEntries) { | ||
48 | $io->success('No entry to reload.'); | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | $io->note( | ||
54 | sprintf( | ||
55 | "You're going to reload %s entries. Depending on the number of entry to reload, this could be a very long process.", | ||
56 | $nbEntries | ||
57 | ) | ||
58 | ); | ||
59 | |||
60 | if (!$io->confirm('Are you sure you want to proceed?')) { | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | $progressBar = $io->createProgressBar($nbEntries); | ||
65 | |||
66 | $contentProxy = $this->getContainer()->get('wallabag_core.content_proxy'); | ||
67 | $em = $this->getContainer()->get('doctrine')->getManager(); | ||
68 | $dispatcher = $this->getContainer()->get('event_dispatcher'); | ||
69 | |||
70 | $progressBar->start(); | ||
71 | foreach ($entryIds as $entryId) { | ||
72 | $entry = $entryRepository->find($entryId); | ||
73 | |||
74 | $contentProxy->updateEntry($entry, $entry->getUrl()); | ||
75 | $em->persist($entry); | ||
76 | $em->flush(); | ||
77 | |||
78 | $dispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
79 | $progressBar->advance(); | ||
80 | |||
81 | $em->detach($entry); | ||
82 | } | ||
83 | $progressBar->finish(); | ||
84 | |||
85 | $io->newLine(2); | ||
86 | $io->success('Done.'); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/ShowUserCommand.php b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php new file mode 100644 index 00000000..a0184267 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php | |||
@@ -0,0 +1,75 @@ | |||
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 Symfony\Component\Console\Style\SymfonyStyle; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | |||
13 | class ShowUserCommand extends ContainerAwareCommand | ||
14 | { | ||
15 | /** @var SymfonyStyle */ | ||
16 | protected $io; | ||
17 | |||
18 | protected function configure() | ||
19 | { | ||
20 | $this | ||
21 | ->setName('wallabag:user:show') | ||
22 | ->setDescription('Show user details') | ||
23 | ->setHelp('This command shows the details for an user') | ||
24 | ->addArgument( | ||
25 | 'username', | ||
26 | InputArgument::REQUIRED, | ||
27 | 'User to show details for' | ||
28 | ); | ||
29 | } | ||
30 | |||
31 | protected function execute(InputInterface $input, OutputInterface $output) | ||
32 | { | ||
33 | $this->io = new SymfonyStyle($input, $output); | ||
34 | |||
35 | $username = $input->getArgument('username'); | ||
36 | |||
37 | try { | ||
38 | $user = $this->getUser($username); | ||
39 | $this->showUser($user); | ||
40 | } catch (NoResultException $e) { | ||
41 | $this->io->error(sprintf('User "%s" not found.', $username)); | ||
42 | |||
43 | return 1; | ||
44 | } | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * @param User $user | ||
51 | */ | ||
52 | private function showUser(User $user) | ||
53 | { | ||
54 | $this->io->listing([ | ||
55 | sprintf('Username: %s', $user->getUsername()), | ||
56 | sprintf('Email: %s', $user->getEmail()), | ||
57 | sprintf('Display name: %s', $user->getName()), | ||
58 | sprintf('Creation date: %s', $user->getCreatedAt()->format('Y-m-d H:i:s')), | ||
59 | sprintf('Last login: %s', null !== $user->getLastLogin() ? $user->getLastLogin()->format('Y-m-d H:i:s') : 'never'), | ||
60 | sprintf('2FA activated: %s', $user->isTwoFactorAuthentication() ? 'yes' : 'no'), | ||
61 | ]); | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * Fetches a user from its username. | ||
66 | * | ||
67 | * @param string $username | ||
68 | * | ||
69 | * @return \Wallabag\UserBundle\Entity\User | ||
70 | */ | ||
71 | private function getUser($username) | ||
72 | { | ||
73 | return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); | ||
74 | } | ||
75 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php index 3f9bb04d..4afac2d4 100644 --- a/src/Wallabag/CoreBundle/Command/TagAllCommand.php +++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php | |||
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | |||
7 | use Symfony\Component\Console\Input\InputArgument; | 7 | use Symfony\Component\Console\Input\InputArgument; |
8 | use Symfony\Component\Console\Input\InputInterface; | 8 | use Symfony\Component\Console\Input\InputInterface; |
9 | use Symfony\Component\Console\Output\OutputInterface; | 9 | use Symfony\Component\Console\Output\OutputInterface; |
10 | use Symfony\Component\Console\Style\SymfonyStyle; | ||
10 | 11 | ||
11 | class TagAllCommand extends ContainerAwareCommand | 12 | class TagAllCommand extends ContainerAwareCommand |
12 | { | 13 | { |
@@ -25,21 +26,22 @@ class TagAllCommand extends ContainerAwareCommand | |||
25 | 26 | ||
26 | protected function execute(InputInterface $input, OutputInterface $output) | 27 | protected function execute(InputInterface $input, OutputInterface $output) |
27 | { | 28 | { |
29 | $io = new SymfonyStyle($input, $output); | ||
30 | |||
28 | try { | 31 | try { |
29 | $user = $this->getUser($input->getArgument('username')); | 32 | $user = $this->getUser($input->getArgument('username')); |
30 | } catch (NoResultException $e) { | 33 | } catch (NoResultException $e) { |
31 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); | 34 | $io->error(sprintf('User "%s" not found.', $input->getArgument('username'))); |
32 | 35 | ||
33 | return 1; | 36 | return 1; |
34 | } | 37 | } |
35 | $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger'); | 38 | $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger'); |
36 | 39 | ||
37 | $output->write(sprintf('Tagging entries for user « <comment>%s</comment> »... ', $user->getUserName())); | 40 | $io->text(sprintf('Tagging entries for user <info>%s</info>...', $user->getUserName())); |
38 | 41 | ||
39 | $entries = $tagger->tagAllForUser($user); | 42 | $entries = $tagger->tagAllForUser($user); |
40 | 43 | ||
41 | $output->writeln('<info>Done.</info>'); | 44 | $io->text('Persist entries... '); |
42 | $output->write(sprintf('Persist entries ... ', $user->getUserName())); | ||
43 | 45 | ||
44 | $em = $this->getDoctrine()->getManager(); | 46 | $em = $this->getDoctrine()->getManager(); |
45 | foreach ($entries as $entry) { | 47 | foreach ($entries as $entry) { |
@@ -47,7 +49,9 @@ class TagAllCommand extends ContainerAwareCommand | |||
47 | } | 49 | } |
48 | $em->flush(); | 50 | $em->flush(); |
49 | 51 | ||
50 | $output->writeln('<info>Done.</info>'); | 52 | $io->success('Done.'); |
53 | |||
54 | return 0; | ||
51 | } | 55 | } |
52 | 56 | ||
53 | /** | 57 | /** |
@@ -59,7 +63,7 @@ class TagAllCommand extends ContainerAwareCommand | |||
59 | */ | 63 | */ |
60 | private function getUser($username) | 64 | private function getUser($username) |
61 | { | 65 | { |
62 | return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); | 66 | return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); |
63 | } | 67 | } |
64 | 68 | ||
65 | private function getDoctrine() | 69 | private function getDoctrine() |
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 907bf78e..a89bb780 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php | |||
@@ -10,8 +10,8 @@ use Symfony\Component\HttpFoundation\Request; | |||
10 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | 10 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; |
11 | use Wallabag\CoreBundle\Entity\Config; | 11 | use Wallabag\CoreBundle\Entity\Config; |
12 | use Wallabag\CoreBundle\Entity\TaggingRule; | 12 | use Wallabag\CoreBundle\Entity\TaggingRule; |
13 | use Wallabag\CoreBundle\Form\Type\ConfigType; | ||
14 | use Wallabag\CoreBundle\Form\Type\ChangePasswordType; | 13 | use Wallabag\CoreBundle\Form\Type\ChangePasswordType; |
14 | use Wallabag\CoreBundle\Form\Type\ConfigType; | ||
15 | use Wallabag\CoreBundle\Form\Type\RssType; | 15 | use Wallabag\CoreBundle\Form\Type\RssType; |
16 | use Wallabag\CoreBundle\Form\Type\TaggingRuleType; | 16 | use Wallabag\CoreBundle\Form\Type\TaggingRuleType; |
17 | use Wallabag\CoreBundle\Form\Type\UserInformationType; | 17 | use Wallabag\CoreBundle\Form\Type\UserInformationType; |
@@ -54,7 +54,7 @@ class ConfigController extends Controller | |||
54 | } | 54 | } |
55 | 55 | ||
56 | // handle changing password | 56 | // handle changing password |
57 | $pwdForm = $this->createForm(ChangePasswordType::class, null, ['action' => $this->generateUrl('config').'#set4']); | 57 | $pwdForm = $this->createForm(ChangePasswordType::class, null, ['action' => $this->generateUrl('config') . '#set4']); |
58 | $pwdForm->handleRequest($request); | 58 | $pwdForm->handleRequest($request); |
59 | 59 | ||
60 | if ($pwdForm->isSubmitted() && $pwdForm->isValid()) { | 60 | if ($pwdForm->isSubmitted() && $pwdForm->isValid()) { |
@@ -69,13 +69,13 @@ class ConfigController extends Controller | |||
69 | 69 | ||
70 | $this->get('session')->getFlashBag()->add('notice', $message); | 70 | $this->get('session')->getFlashBag()->add('notice', $message); |
71 | 71 | ||
72 | return $this->redirect($this->generateUrl('config').'#set4'); | 72 | return $this->redirect($this->generateUrl('config') . '#set4'); |
73 | } | 73 | } |
74 | 74 | ||
75 | // handle changing user information | 75 | // handle changing user information |
76 | $userForm = $this->createForm(UserInformationType::class, $user, [ | 76 | $userForm = $this->createForm(UserInformationType::class, $user, [ |
77 | 'validation_groups' => ['Profile'], | 77 | 'validation_groups' => ['Profile'], |
78 | 'action' => $this->generateUrl('config').'#set3', | 78 | 'action' => $this->generateUrl('config') . '#set3', |
79 | ]); | 79 | ]); |
80 | $userForm->handleRequest($request); | 80 | $userForm->handleRequest($request); |
81 | 81 | ||
@@ -87,11 +87,11 @@ class ConfigController extends Controller | |||
87 | 'flashes.config.notice.user_updated' | 87 | 'flashes.config.notice.user_updated' |
88 | ); | 88 | ); |
89 | 89 | ||
90 | return $this->redirect($this->generateUrl('config').'#set3'); | 90 | return $this->redirect($this->generateUrl('config') . '#set3'); |
91 | } | 91 | } |
92 | 92 | ||
93 | // handle rss information | 93 | // handle rss information |
94 | $rssForm = $this->createForm(RssType::class, $config, ['action' => $this->generateUrl('config').'#set2']); | 94 | $rssForm = $this->createForm(RssType::class, $config, ['action' => $this->generateUrl('config') . '#set2']); |
95 | $rssForm->handleRequest($request); | 95 | $rssForm->handleRequest($request); |
96 | 96 | ||
97 | if ($rssForm->isSubmitted() && $rssForm->isValid()) { | 97 | if ($rssForm->isSubmitted() && $rssForm->isValid()) { |
@@ -103,12 +103,12 @@ class ConfigController extends Controller | |||
103 | 'flashes.config.notice.rss_updated' | 103 | 'flashes.config.notice.rss_updated' |
104 | ); | 104 | ); |
105 | 105 | ||
106 | return $this->redirect($this->generateUrl('config').'#set2'); | 106 | return $this->redirect($this->generateUrl('config') . '#set2'); |
107 | } | 107 | } |
108 | 108 | ||
109 | // handle tagging rule | 109 | // handle tagging rule |
110 | $taggingRule = new TaggingRule(); | 110 | $taggingRule = new TaggingRule(); |
111 | $action = $this->generateUrl('config').'#set5'; | 111 | $action = $this->generateUrl('config') . '#set5'; |
112 | 112 | ||
113 | if ($request->query->has('tagging-rule')) { | 113 | if ($request->query->has('tagging-rule')) { |
114 | $taggingRule = $this->getDoctrine() | 114 | $taggingRule = $this->getDoctrine() |
@@ -119,7 +119,7 @@ class ConfigController extends Controller | |||
119 | return $this->redirect($action); | 119 | return $this->redirect($action); |
120 | } | 120 | } |
121 | 121 | ||
122 | $action = $this->generateUrl('config').'?tagging-rule='.$taggingRule->getId().'#set5'; | 122 | $action = $this->generateUrl('config') . '?tagging-rule=' . $taggingRule->getId() . '#set5'; |
123 | } | 123 | } |
124 | 124 | ||
125 | $newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, ['action' => $action]); | 125 | $newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, ['action' => $action]); |
@@ -135,7 +135,7 @@ class ConfigController extends Controller | |||
135 | 'flashes.config.notice.tagging_rules_updated' | 135 | 'flashes.config.notice.tagging_rules_updated' |
136 | ); | 136 | ); |
137 | 137 | ||
138 | return $this->redirect($this->generateUrl('config').'#set5'); | 138 | return $this->redirect($this->generateUrl('config') . '#set5'); |
139 | } | 139 | } |
140 | 140 | ||
141 | return $this->render('WallabagCoreBundle:Config:index.html.twig', [ | 141 | return $this->render('WallabagCoreBundle:Config:index.html.twig', [ |
@@ -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 | } |
@@ -183,7 +182,7 @@ class ConfigController extends Controller | |||
183 | 'flashes.config.notice.rss_token_updated' | 182 | 'flashes.config.notice.rss_token_updated' |
184 | ); | 183 | ); |
185 | 184 | ||
186 | return $this->redirect($this->generateUrl('config').'#set2'); | 185 | return $this->redirect($this->generateUrl('config') . '#set2'); |
187 | } | 186 | } |
188 | 187 | ||
189 | /** | 188 | /** |
@@ -208,7 +207,7 @@ class ConfigController extends Controller | |||
208 | 'flashes.config.notice.tagging_rules_deleted' | 207 | 'flashes.config.notice.tagging_rules_deleted' |
209 | ); | 208 | ); |
210 | 209 | ||
211 | return $this->redirect($this->generateUrl('config').'#set5'); | 210 | return $this->redirect($this->generateUrl('config') . '#set5'); |
212 | } | 211 | } |
213 | 212 | ||
214 | /** | 213 | /** |
@@ -224,7 +223,7 @@ class ConfigController extends Controller | |||
224 | { | 223 | { |
225 | $this->validateRuleAction($rule); | 224 | $this->validateRuleAction($rule); |
226 | 225 | ||
227 | return $this->redirect($this->generateUrl('config').'?tagging-rule='.$rule->getId().'#set5'); | 226 | return $this->redirect($this->generateUrl('config') . '?tagging-rule=' . $rule->getId() . '#set5'); |
228 | } | 227 | } |
229 | 228 | ||
230 | /** | 229 | /** |
@@ -242,56 +241,114 @@ class ConfigController extends Controller | |||
242 | ->getRepository('WallabagAnnotationBundle:Annotation') | 241 | ->getRepository('WallabagAnnotationBundle:Annotation') |
243 | ->removeAllByUserId($this->getUser()->getId()); | 242 | ->removeAllByUserId($this->getUser()->getId()); |
244 | break; | 243 | break; |
245 | |||
246 | case 'tags': | 244 | case 'tags': |
247 | $this->removeAllTagsByUserId($this->getUser()->getId()); | 245 | $this->removeAllTagsByUserId($this->getUser()->getId()); |
248 | break; | 246 | break; |
249 | |||
250 | case 'entries': | 247 | case 'entries': |
251 | // SQLite doesn't care about cascading remove, so we need to manually remove associated stuf | 248 | // SQLite doesn't care about cascading remove, so we need to manually remove associated stuff |
252 | // otherwise they won't be removed ... | 249 | // otherwise they won't be removed ... |
253 | if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { | 250 | if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { |
254 | $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); | 251 | $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); |
255 | } | 252 | } |
256 | 253 | ||
257 | // manually remove tags to avoid orphan tag | 254 | // manually remove tags to avoid orphan tag |
258 | $this->removeAllTagsByUserId($this->getUser()->getId()); | 255 | $this->removeAllTagsByUserId($this->getUser()->getId()); |
259 | 256 | ||
260 | $this->getDoctrine() | 257 | $this->get('wallabag_core.entry_repository')->removeAllByUserId($this->getUser()->getId()); |
261 | ->getRepository('WallabagCoreBundle:Entry') | 258 | break; |
262 | ->removeAllByUserId($this->getUser()->getId()); | 259 | case 'archived': |
260 | if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { | ||
261 | $this->removeAnnotationsForArchivedByUserId($this->getUser()->getId()); | ||
262 | } | ||
263 | |||
264 | // manually remove tags to avoid orphan tag | ||
265 | $this->removeTagsForArchivedByUserId($this->getUser()->getId()); | ||
266 | |||
267 | $this->get('wallabag_core.entry_repository')->removeArchivedByUserId($this->getUser()->getId()); | ||
268 | break; | ||
263 | } | 269 | } |
264 | 270 | ||
265 | $this->get('session')->getFlashBag()->add( | 271 | $this->get('session')->getFlashBag()->add( |
266 | 'notice', | 272 | 'notice', |
267 | 'flashes.config.notice.'.$type.'_reset' | 273 | 'flashes.config.notice.' . $type . '_reset' |
268 | ); | 274 | ); |
269 | 275 | ||
270 | return $this->redirect($this->generateUrl('config').'#set3'); | 276 | return $this->redirect($this->generateUrl('config') . '#set3'); |
271 | } | 277 | } |
272 | 278 | ||
273 | /** | 279 | /** |
274 | * Remove all tags for a given user and cleanup orphan tags. | 280 | * Delete account for current user. |
275 | * | 281 | * |
276 | * @param int $userId | 282 | * @Route("/account/delete", name="delete_account") |
283 | * | ||
284 | * @param Request $request | ||
285 | * | ||
286 | * @throws AccessDeniedHttpException | ||
287 | * | ||
288 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
277 | */ | 289 | */ |
278 | private function removeAllTagsByUserId($userId) | 290 | public function deleteAccountAction(Request $request) |
291 | { | ||
292 | $enabledUsers = $this->get('wallabag_user.user_repository') | ||
293 | ->getSumEnabledUsers(); | ||
294 | |||
295 | if ($enabledUsers <= 1) { | ||
296 | throw new AccessDeniedHttpException(); | ||
297 | } | ||
298 | |||
299 | $user = $this->getUser(); | ||
300 | |||
301 | // logout current user | ||
302 | $this->get('security.token_storage')->setToken(null); | ||
303 | $request->getSession()->invalidate(); | ||
304 | |||
305 | $em = $this->get('fos_user.user_manager'); | ||
306 | $em->deleteUser($user); | ||
307 | |||
308 | return $this->redirect($this->generateUrl('fos_user_security_login')); | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * Switch view mode for current user. | ||
313 | * | ||
314 | * @Route("/config/view-mode", name="switch_view_mode") | ||
315 | * | ||
316 | * @param Request $request | ||
317 | * | ||
318 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
319 | */ | ||
320 | public function changeViewModeAction(Request $request) | ||
279 | { | 321 | { |
280 | $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId); | 322 | $user = $this->getUser(); |
323 | $user->getConfig()->setListMode(!$user->getConfig()->getListMode()); | ||
324 | |||
325 | $em = $this->getDoctrine()->getManager(); | ||
326 | $em->persist($user); | ||
327 | $em->flush(); | ||
281 | 328 | ||
329 | return $this->redirect($request->headers->get('referer')); | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * Remove all tags for given tags and a given user and cleanup orphan tags. | ||
334 | * | ||
335 | * @param array $tags | ||
336 | * @param int $userId | ||
337 | */ | ||
338 | private function removeAllTagsByStatusAndUserId($tags, $userId) | ||
339 | { | ||
282 | if (empty($tags)) { | 340 | if (empty($tags)) { |
283 | return; | 341 | return; |
284 | } | 342 | } |
285 | 343 | ||
286 | $this->getDoctrine() | 344 | $this->get('wallabag_core.entry_repository') |
287 | ->getRepository('WallabagCoreBundle:Entry') | ||
288 | ->removeTags($userId, $tags); | 345 | ->removeTags($userId, $tags); |
289 | 346 | ||
290 | // cleanup orphan tags | 347 | // cleanup orphan tags |
291 | $em = $this->getDoctrine()->getManager(); | 348 | $em = $this->getDoctrine()->getManager(); |
292 | 349 | ||
293 | foreach ($tags as $tag) { | 350 | foreach ($tags as $tag) { |
294 | if (count($tag->getEntries()) === 0) { | 351 | if (0 === count($tag->getEntries())) { |
295 | $em->remove($tag); | 352 | $em->remove($tag); |
296 | } | 353 | } |
297 | } | 354 | } |
@@ -300,13 +357,50 @@ class ConfigController extends Controller | |||
300 | } | 357 | } |
301 | 358 | ||
302 | /** | 359 | /** |
360 | * Remove all tags for a given user and cleanup orphan tags. | ||
361 | * | ||
362 | * @param int $userId | ||
363 | */ | ||
364 | private function removeAllTagsByUserId($userId) | ||
365 | { | ||
366 | $tags = $this->get('wallabag_core.tag_repository')->findAllTags($userId); | ||
367 | $this->removeAllTagsByStatusAndUserId($tags, $userId); | ||
368 | } | ||
369 | |||
370 | /** | ||
371 | * Remove all tags for a given user and cleanup orphan tags. | ||
372 | * | ||
373 | * @param int $userId | ||
374 | */ | ||
375 | private function removeTagsForArchivedByUserId($userId) | ||
376 | { | ||
377 | $tags = $this->get('wallabag_core.tag_repository')->findForArchivedArticlesByUser($userId); | ||
378 | $this->removeAllTagsByStatusAndUserId($tags, $userId); | ||
379 | } | ||
380 | |||
381 | private function removeAnnotationsForArchivedByUserId($userId) | ||
382 | { | ||
383 | $em = $this->getDoctrine()->getManager(); | ||
384 | |||
385 | $archivedEntriesAnnotations = $this->getDoctrine() | ||
386 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
387 | ->findAllArchivedEntriesByUser($userId); | ||
388 | |||
389 | foreach ($archivedEntriesAnnotations as $archivedEntriesAnnotation) { | ||
390 | $em->remove($archivedEntriesAnnotation); | ||
391 | } | ||
392 | |||
393 | $em->flush(); | ||
394 | } | ||
395 | |||
396 | /** | ||
303 | * Validate that a rule can be edited/deleted by the current user. | 397 | * Validate that a rule can be edited/deleted by the current user. |
304 | * | 398 | * |
305 | * @param TaggingRule $rule | 399 | * @param TaggingRule $rule |
306 | */ | 400 | */ |
307 | private function validateRuleAction(TaggingRule $rule) | 401 | private function validateRuleAction(TaggingRule $rule) |
308 | { | 402 | { |
309 | if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) { | 403 | if ($this->getUser()->getId() !== $rule->getConfig()->getUser()->getId()) { |
310 | throw $this->createAccessDeniedException('You can not access this tagging rule.'); | 404 | throw $this->createAccessDeniedException('You can not access this tagging rule.'); |
311 | } | 405 | } |
312 | } | 406 | } |
@@ -330,58 +424,4 @@ class ConfigController extends Controller | |||
330 | 424 | ||
331 | return $config; | 425 | return $config; |
332 | } | 426 | } |
333 | |||
334 | /** | ||
335 | * Delete account for current user. | ||
336 | * | ||
337 | * @Route("/account/delete", name="delete_account") | ||
338 | * | ||
339 | * @param Request $request | ||
340 | * | ||
341 | * @throws AccessDeniedHttpException | ||
342 | * | ||
343 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
344 | */ | ||
345 | public function deleteAccountAction(Request $request) | ||
346 | { | ||
347 | $enabledUsers = $this->getDoctrine() | ||
348 | ->getRepository('WallabagUserBundle:User') | ||
349 | ->getSumEnabledUsers(); | ||
350 | |||
351 | if ($enabledUsers <= 1) { | ||
352 | throw new AccessDeniedHttpException(); | ||
353 | } | ||
354 | |||
355 | $user = $this->getUser(); | ||
356 | |||
357 | // logout current user | ||
358 | $this->get('security.token_storage')->setToken(null); | ||
359 | $request->getSession()->invalidate(); | ||
360 | |||
361 | $em = $this->get('fos_user.user_manager'); | ||
362 | $em->deleteUser($user); | ||
363 | |||
364 | return $this->redirect($this->generateUrl('fos_user_security_login')); | ||
365 | } | ||
366 | |||
367 | /** | ||
368 | * Switch view mode for current user. | ||
369 | * | ||
370 | * @Route("/config/view-mode", name="switch_view_mode") | ||
371 | * | ||
372 | * @param Request $request | ||
373 | * | ||
374 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
375 | */ | ||
376 | public function changeViewModeAction(Request $request) | ||
377 | { | ||
378 | $user = $this->getUser(); | ||
379 | $user->getConfig()->setListMode(!$user->getConfig()->getListMode()); | ||
380 | |||
381 | $em = $this->getDoctrine()->getManager(); | ||
382 | $em->persist($user); | ||
383 | $em->flush(); | ||
384 | |||
385 | return $this->redirect($request->headers->get('referer')); | ||
386 | } | ||
387 | } | 427 | } |
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php index f7398e69..840dc254 100644 --- a/src/Wallabag/CoreBundle/Controller/EntryController.php +++ b/src/Wallabag/CoreBundle/Controller/EntryController.php | |||
@@ -4,17 +4,17 @@ namespace Wallabag\CoreBundle\Controller; | |||
4 | 4 | ||
5 | use Pagerfanta\Adapter\DoctrineORMAdapter; | 5 | use Pagerfanta\Adapter\DoctrineORMAdapter; |
6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | 6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; |
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; | ||
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 9 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
9 | use Symfony\Component\HttpFoundation\Request; | 10 | use Symfony\Component\HttpFoundation\Request; |
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 11 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
11 | use Wallabag\CoreBundle\Entity\Entry; | 12 | use Wallabag\CoreBundle\Entity\Entry; |
12 | use Wallabag\CoreBundle\Form\Type\EntryFilterType; | 13 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; |
14 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
13 | use Wallabag\CoreBundle\Form\Type\EditEntryType; | 15 | use Wallabag\CoreBundle\Form\Type\EditEntryType; |
16 | use Wallabag\CoreBundle\Form\Type\EntryFilterType; | ||
14 | use Wallabag\CoreBundle\Form\Type\NewEntryType; | 17 | use Wallabag\CoreBundle\Form\Type\NewEntryType; |
15 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; | ||
16 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
17 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | ||
18 | use Wallabag\CoreBundle\Form\Type\SearchEntryType; | 18 | use Wallabag\CoreBundle\Form\Type\SearchEntryType; |
19 | 19 | ||
20 | class EntryController extends Controller | 20 | class EntryController extends Controller |
@@ -52,38 +52,6 @@ class EntryController extends Controller | |||
52 | } | 52 | } |
53 | 53 | ||
54 | /** | 54 | /** |
55 | * Fetch content and update entry. | ||
56 | * In case it fails, entry will return to avod loosing the data. | ||
57 | * | ||
58 | * @param Entry $entry | ||
59 | * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded | ||
60 | * | ||
61 | * @return Entry | ||
62 | */ | ||
63 | private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') | ||
64 | { | ||
65 | // put default title in case of fetching content failed | ||
66 | $entry->setTitle('No title found'); | ||
67 | |||
68 | $message = 'flashes.entry.notice.'.$prefixMessage; | ||
69 | |||
70 | try { | ||
71 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | ||
72 | } catch (\Exception $e) { | ||
73 | $this->get('logger')->error('Error while saving an entry', [ | ||
74 | 'exception' => $e, | ||
75 | 'entry' => $entry, | ||
76 | ]); | ||
77 | |||
78 | $message = 'flashes.entry.notice.'.$prefixMessage.'_failed'; | ||
79 | } | ||
80 | |||
81 | $this->get('session')->getFlashBag()->add('notice', $message); | ||
82 | |||
83 | return $entry; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * @param Request $request | 55 | * @param Request $request |
88 | * | 56 | * |
89 | * @Route("/new-entry", name="new_entry") | 57 | * @Route("/new-entry", name="new_entry") |
@@ -227,7 +195,7 @@ class EntryController extends Controller | |||
227 | public function showUnreadAction(Request $request, $page) | 195 | public function showUnreadAction(Request $request, $page) |
228 | { | 196 | { |
229 | // load the quickstart if no entry in database | 197 | // load the quickstart if no entry in database |
230 | if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUsername($this->getUser()->getId()) == 0) { | 198 | if (1 === (int) $page && 0 === $this->get('wallabag_core.entry_repository')->countAllEntriesByUser($this->getUser()->getId())) { |
231 | return $this->redirect($this->generateUrl('quickstart')); | 199 | return $this->redirect($this->generateUrl('quickstart')); |
232 | } | 200 | } |
233 | 201 | ||
@@ -265,84 +233,6 @@ class EntryController extends Controller | |||
265 | } | 233 | } |
266 | 234 | ||
267 | /** | 235 | /** |
268 | * Global method to retrieve entries depending on the given type | ||
269 | * It returns the response to be send. | ||
270 | * | ||
271 | * @param string $type Entries type: unread, starred or archive | ||
272 | * @param Request $request | ||
273 | * @param int $page | ||
274 | * | ||
275 | * @return \Symfony\Component\HttpFoundation\Response | ||
276 | */ | ||
277 | private function showEntries($type, Request $request, $page) | ||
278 | { | ||
279 | $repository = $this->get('wallabag_core.entry_repository'); | ||
280 | $searchTerm = (isset($request->get('search_entry')['term']) ? $request->get('search_entry')['term'] : ''); | ||
281 | $currentRoute = (!is_null($request->query->get('currentRoute')) ? $request->query->get('currentRoute') : ''); | ||
282 | |||
283 | switch ($type) { | ||
284 | case 'search': | ||
285 | $qb = $repository->getBuilderForSearchByUser($this->getUser()->getId(), $searchTerm, $currentRoute); | ||
286 | |||
287 | break; | ||
288 | case 'untagged': | ||
289 | $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId()); | ||
290 | |||
291 | break; | ||
292 | case 'starred': | ||
293 | $qb = $repository->getBuilderForStarredByUser($this->getUser()->getId()); | ||
294 | break; | ||
295 | |||
296 | case 'archive': | ||
297 | $qb = $repository->getBuilderForArchiveByUser($this->getUser()->getId()); | ||
298 | break; | ||
299 | |||
300 | case 'unread': | ||
301 | $qb = $repository->getBuilderForUnreadByUser($this->getUser()->getId()); | ||
302 | break; | ||
303 | |||
304 | case 'all': | ||
305 | $qb = $repository->getBuilderForAllByUser($this->getUser()->getId()); | ||
306 | break; | ||
307 | |||
308 | default: | ||
309 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); | ||
310 | } | ||
311 | |||
312 | $form = $this->createForm(EntryFilterType::class); | ||
313 | |||
314 | if ($request->query->has($form->getName())) { | ||
315 | // manually bind values from the request | ||
316 | $form->submit($request->query->get($form->getName())); | ||
317 | |||
318 | // build the query from the given form object | ||
319 | $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $qb); | ||
320 | } | ||
321 | |||
322 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
323 | |||
324 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') | ||
325 | ->prepare($pagerAdapter, $page); | ||
326 | |||
327 | try { | ||
328 | $entries->setCurrentPage($page); | ||
329 | } catch (OutOfRangeCurrentPageException $e) { | ||
330 | if ($page > 1) { | ||
331 | return $this->redirect($this->generateUrl($type, ['page' => $entries->getNbPages()]), 302); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | return $this->render( | ||
336 | 'WallabagCoreBundle:Entry:entries.html.twig', [ | ||
337 | 'form' => $form->createView(), | ||
338 | 'entries' => $entries, | ||
339 | 'currentPage' => $page, | ||
340 | 'searchTerm' => $searchTerm, | ||
341 | ] | ||
342 | ); | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * Shows entry content. | 236 | * Shows entry content. |
347 | * | 237 | * |
348 | * @param Entry $entry | 238 | * @param Entry $entry |
@@ -443,6 +333,7 @@ class EntryController extends Controller | |||
443 | $this->checkUserAction($entry); | 333 | $this->checkUserAction($entry); |
444 | 334 | ||
445 | $entry->toggleStar(); | 335 | $entry->toggleStar(); |
336 | $entry->updateStar($entry->isStarred()); | ||
446 | $this->getDoctrine()->getManager()->flush(); | 337 | $this->getDoctrine()->getManager()->flush(); |
447 | 338 | ||
448 | $message = 'flashes.entry.notice.entry_unstarred'; | 339 | $message = 'flashes.entry.notice.entry_unstarred'; |
@@ -495,7 +386,7 @@ class EntryController extends Controller | |||
495 | 386 | ||
496 | // don't redirect user to the deleted entry (check that the referer doesn't end with the same url) | 387 | // don't redirect user to the deleted entry (check that the referer doesn't end with the same url) |
497 | $referer = $request->headers->get('referer'); | 388 | $referer = $request->headers->get('referer'); |
498 | $to = (1 !== preg_match('#'.$url.'$#i', $referer) ? $referer : null); | 389 | $to = (1 !== preg_match('#' . $url . '$#i', $referer) ? $referer : null); |
499 | 390 | ||
500 | $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($to); | 391 | $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($to); |
501 | 392 | ||
@@ -503,30 +394,6 @@ class EntryController extends Controller | |||
503 | } | 394 | } |
504 | 395 | ||
505 | /** | 396 | /** |
506 | * Check if the logged user can manage the given entry. | ||
507 | * | ||
508 | * @param Entry $entry | ||
509 | */ | ||
510 | private function checkUserAction(Entry $entry) | ||
511 | { | ||
512 | if (null === $this->getUser() || $this->getUser()->getId() != $entry->getUser()->getId()) { | ||
513 | throw $this->createAccessDeniedException('You can not access this entry.'); | ||
514 | } | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * Check for existing entry, if it exists, redirect to it with a message. | ||
519 | * | ||
520 | * @param Entry $entry | ||
521 | * | ||
522 | * @return Entry|bool | ||
523 | */ | ||
524 | private function checkIfEntryAlreadyExists(Entry $entry) | ||
525 | { | ||
526 | return $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($entry->getUrl(), $this->getUser()->getId()); | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * Get public URL for entry (and generate it if necessary). | 397 | * Get public URL for entry (and generate it if necessary). |
531 | * | 398 | * |
532 | * @param Entry $entry | 399 | * @param Entry $entry |
@@ -612,4 +479,127 @@ class EntryController extends Controller | |||
612 | { | 479 | { |
613 | return $this->showEntries('untagged', $request, $page); | 480 | return $this->showEntries('untagged', $request, $page); |
614 | } | 481 | } |
482 | |||
483 | /** | ||
484 | * Fetch content and update entry. | ||
485 | * In case it fails, $entry->getContent will return an error message. | ||
486 | * | ||
487 | * @param Entry $entry | ||
488 | * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded | ||
489 | */ | ||
490 | private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') | ||
491 | { | ||
492 | $message = 'flashes.entry.notice.' . $prefixMessage; | ||
493 | |||
494 | try { | ||
495 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); | ||
496 | } catch (\Exception $e) { | ||
497 | $this->get('logger')->error('Error while saving an entry', [ | ||
498 | 'exception' => $e, | ||
499 | 'entry' => $entry, | ||
500 | ]); | ||
501 | |||
502 | $message = 'flashes.entry.notice.' . $prefixMessage . '_failed'; | ||
503 | } | ||
504 | |||
505 | $this->get('session')->getFlashBag()->add('notice', $message); | ||
506 | } | ||
507 | |||
508 | /** | ||
509 | * Global method to retrieve entries depending on the given type | ||
510 | * It returns the response to be send. | ||
511 | * | ||
512 | * @param string $type Entries type: unread, starred or archive | ||
513 | * @param Request $request | ||
514 | * @param int $page | ||
515 | * | ||
516 | * @return \Symfony\Component\HttpFoundation\Response | ||
517 | */ | ||
518 | private function showEntries($type, Request $request, $page) | ||
519 | { | ||
520 | $repository = $this->get('wallabag_core.entry_repository'); | ||
521 | $searchTerm = (isset($request->get('search_entry')['term']) ? $request->get('search_entry')['term'] : ''); | ||
522 | $currentRoute = (null !== $request->query->get('currentRoute') ? $request->query->get('currentRoute') : ''); | ||
523 | |||
524 | switch ($type) { | ||
525 | case 'search': | ||
526 | $qb = $repository->getBuilderForSearchByUser($this->getUser()->getId(), $searchTerm, $currentRoute); | ||
527 | |||
528 | break; | ||
529 | case 'untagged': | ||
530 | $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId()); | ||
531 | |||
532 | break; | ||
533 | case 'starred': | ||
534 | $qb = $repository->getBuilderForStarredByUser($this->getUser()->getId()); | ||
535 | break; | ||
536 | case 'archive': | ||
537 | $qb = $repository->getBuilderForArchiveByUser($this->getUser()->getId()); | ||
538 | break; | ||
539 | case 'unread': | ||
540 | $qb = $repository->getBuilderForUnreadByUser($this->getUser()->getId()); | ||
541 | break; | ||
542 | case 'all': | ||
543 | $qb = $repository->getBuilderForAllByUser($this->getUser()->getId()); | ||
544 | break; | ||
545 | default: | ||
546 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); | ||
547 | } | ||
548 | |||
549 | $form = $this->createForm(EntryFilterType::class); | ||
550 | |||
551 | if ($request->query->has($form->getName())) { | ||
552 | // manually bind values from the request | ||
553 | $form->submit($request->query->get($form->getName())); | ||
554 | |||
555 | // build the query from the given form object | ||
556 | $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $qb); | ||
557 | } | ||
558 | |||
559 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
560 | |||
561 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter); | ||
562 | |||
563 | try { | ||
564 | $entries->setCurrentPage($page); | ||
565 | } catch (OutOfRangeCurrentPageException $e) { | ||
566 | if ($page > 1) { | ||
567 | return $this->redirect($this->generateUrl($type, ['page' => $entries->getNbPages()]), 302); | ||
568 | } | ||
569 | } | ||
570 | |||
571 | return $this->render( | ||
572 | 'WallabagCoreBundle:Entry:entries.html.twig', [ | ||
573 | 'form' => $form->createView(), | ||
574 | 'entries' => $entries, | ||
575 | 'currentPage' => $page, | ||
576 | 'searchTerm' => $searchTerm, | ||
577 | 'isFiltered' => $form->isSubmitted(), | ||
578 | ] | ||
579 | ); | ||
580 | } | ||
581 | |||
582 | /** | ||
583 | * Check if the logged user can manage the given entry. | ||
584 | * | ||
585 | * @param Entry $entry | ||
586 | */ | ||
587 | private function checkUserAction(Entry $entry) | ||
588 | { | ||
589 | if (null === $this->getUser() || $this->getUser()->getId() !== $entry->getUser()->getId()) { | ||
590 | throw $this->createAccessDeniedException('You can not access this entry.'); | ||
591 | } | ||
592 | } | ||
593 | |||
594 | /** | ||
595 | * Check for existing entry, if it exists, redirect to it with a message. | ||
596 | * | ||
597 | * @param Entry $entry | ||
598 | * | ||
599 | * @return Entry|bool | ||
600 | */ | ||
601 | private function checkIfEntryAlreadyExists(Entry $entry) | ||
602 | { | ||
603 | return $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($entry->getUrl(), $this->getUser()->getId()); | ||
604 | } | ||
615 | } | 605 | } |
diff --git a/src/Wallabag/CoreBundle/Controller/ExceptionController.php b/src/Wallabag/CoreBundle/Controller/ExceptionController.php index abfa9c2f..461309ea 100644 --- a/src/Wallabag/CoreBundle/Controller/ExceptionController.php +++ b/src/Wallabag/CoreBundle/Controller/ExceptionController.php | |||
@@ -14,7 +14,7 @@ class ExceptionController extends BaseExceptionController | |||
14 | protected function findTemplate(Request $request, $format, $code, $showException) | 14 | protected function findTemplate(Request $request, $format, $code, $showException) |
15 | { | 15 | { |
16 | $name = $showException ? 'exception' : 'error'; | 16 | $name = $showException ? 'exception' : 'error'; |
17 | if ($showException && 'html' == $format) { | 17 | if ($showException && 'html' === $format) { |
18 | $name = 'exception_full'; | 18 | $name = 'exception_full'; |
19 | } | 19 | } |
20 | 20 | ||
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php index abc3336a..7ca89239 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. |
@@ -34,6 +33,7 @@ class ExportController extends Controller | |||
34 | return $this->get('wallabag_core.helper.entries_export') | 33 | return $this->get('wallabag_core.helper.entries_export') |
35 | ->setEntries($entry) | 34 | ->setEntries($entry) |
36 | ->updateTitle('entry') | 35 | ->updateTitle('entry') |
36 | ->updateAuthor('entry') | ||
37 | ->exportAs($format); | 37 | ->exportAs($format); |
38 | } catch (\InvalidArgumentException $e) { | 38 | } catch (\InvalidArgumentException $e) { |
39 | throw new NotFoundHttpException($e->getMessage()); | 39 | throw new NotFoundHttpException($e->getMessage()); |
@@ -56,17 +56,18 @@ class ExportController extends Controller | |||
56 | public function downloadEntriesAction(Request $request, $format, $category) | 56 | public function downloadEntriesAction(Request $request, $format, $category) |
57 | { | 57 | { |
58 | $method = ucfirst($category); | 58 | $method = ucfirst($category); |
59 | $methodBuilder = 'getBuilderFor'.$method.'ByUser'; | 59 | $methodBuilder = 'getBuilderFor' . $method . 'ByUser'; |
60 | $repository = $this->get('wallabag_core.entry_repository'); | ||
60 | 61 | ||
61 | if ($category == 'tag_entries') { | 62 | if ('tag_entries' === $category) { |
62 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneBySlug($request->query->get('tag')); | 63 | $tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag')); |
63 | 64 | ||
64 | $entries = $this->getDoctrine() | 65 | $entries = $repository->findAllByTagId( |
65 | ->getRepository('WallabagCoreBundle:Entry') | 66 | $this->getUser()->getId(), |
66 | ->findAllByTagId($this->getUser()->getId(), $tag->getId()); | 67 | $tag->getId() |
68 | ); | ||
67 | } else { | 69 | } else { |
68 | $entries = $this->getDoctrine() | 70 | $entries = $repository |
69 | ->getRepository('WallabagCoreBundle:Entry') | ||
70 | ->$methodBuilder($this->getUser()->getId()) | 71 | ->$methodBuilder($this->getUser()->getId()) |
71 | ->getQuery() | 72 | ->getQuery() |
72 | ->getResult(); | 73 | ->getResult(); |
@@ -76,6 +77,7 @@ class ExportController extends Controller | |||
76 | return $this->get('wallabag_core.helper.entries_export') | 77 | return $this->get('wallabag_core.helper.entries_export') |
77 | ->setEntries($entries) | 78 | ->setEntries($entries) |
78 | ->updateTitle($method) | 79 | ->updateTitle($method) |
80 | ->updateAuthor($method) | ||
79 | ->exportAs($format); | 81 | ->exportAs($format); |
80 | } catch (\InvalidArgumentException $e) { | 82 | } catch (\InvalidArgumentException $e) { |
81 | throw new NotFoundHttpException($e->getMessage()); | 83 | throw new NotFoundHttpException($e->getMessage()); |
diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php index 92f18707..e84044b1 100644 --- a/src/Wallabag/CoreBundle/Controller/RssController.php +++ b/src/Wallabag/CoreBundle/Controller/RssController.php | |||
@@ -2,16 +2,19 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Controller; | 3 | namespace Wallabag\CoreBundle\Controller; |
4 | 4 | ||
5 | use Pagerfanta\Adapter\ArrayAdapter; | ||
5 | use Pagerfanta\Adapter\DoctrineORMAdapter; | 6 | use Pagerfanta\Adapter\DoctrineORMAdapter; |
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\Bundle\FrameworkBundle\Controller\Controller; | 11 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
12 | use Symfony\Component\HttpFoundation\Request; | ||
13 | use Symfony\Component\HttpFoundation\Response; | ||
14 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
12 | use Wallabag\CoreBundle\Entity\Entry; | 15 | use Wallabag\CoreBundle\Entity\Entry; |
16 | use Wallabag\CoreBundle\Entity\Tag; | ||
13 | use Wallabag\UserBundle\Entity\User; | 17 | use Wallabag\UserBundle\Entity\User; |
14 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
15 | 18 | ||
16 | class RssController extends Controller | 19 | class RssController extends Controller |
17 | { | 20 | { |
@@ -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,21 +141,21 @@ 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': |
73 | $qb = $repository->getBuilderForStarredByUser($user->getId()); | 148 | $qb = $repository->getBuilderForStarredByUser($user->getId()); |
74 | break; | 149 | break; |
75 | |||
76 | case 'archive': | 150 | case 'archive': |
77 | $qb = $repository->getBuilderForArchiveByUser($user->getId()); | 151 | $qb = $repository->getBuilderForArchiveByUser($user->getId()); |
78 | break; | 152 | break; |
79 | |||
80 | case 'unread': | 153 | case 'unread': |
81 | $qb = $repository->getBuilderForUnreadByUser($user->getId()); | 154 | $qb = $repository->getBuilderForUnreadByUser($user->getId()); |
82 | break; | 155 | break; |
83 | 156 | case 'all': | |
157 | $qb = $repository->getBuilderForAllByUser($user->getId()); | ||
158 | break; | ||
84 | default: | 159 | default: |
85 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); | 160 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); |
86 | } | 161 | } |
@@ -92,7 +167,7 @@ class RssController extends Controller | |||
92 | $entries->setMaxPerPage($perPage); | 167 | $entries->setMaxPerPage($perPage); |
93 | 168 | ||
94 | $url = $this->generateUrl( | 169 | $url = $this->generateUrl( |
95 | $type.'_rss', | 170 | $type . '_rss', |
96 | [ | 171 | [ |
97 | 'username' => $user->getUsername(), | 172 | 'username' => $user->getUsername(), |
98 | 'token' => $user->getConfig()->getRssToken(), | 173 | 'token' => $user->getConfig()->getRssToken(), |
@@ -104,14 +179,19 @@ class RssController extends Controller | |||
104 | $entries->setCurrentPage((int) $page); | 179 | $entries->setCurrentPage((int) $page); |
105 | } catch (OutOfRangeCurrentPageException $e) { | 180 | } catch (OutOfRangeCurrentPageException $e) { |
106 | if ($page > 1) { | 181 | if ($page > 1) { |
107 | return $this->redirect($url.'?page='.$entries->getNbPages(), 302); | 182 | return $this->redirect($url . '?page=' . $entries->getNbPages(), 302); |
108 | } | 183 | } |
109 | } | 184 | } |
110 | 185 | ||
111 | return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ | 186 | return $this->render( |
112 | 'type' => $type, | 187 | '@WallabagCore/themes/common/Entry/entries.xml.twig', |
113 | 'url' => $url, | 188 | [ |
114 | 'entries' => $entries, | 189 | 'url_html' => $this->generateUrl($type, [], UrlGeneratorInterface::ABSOLUTE_URL), |
115 | ]); | 190 | 'type' => $type, |
191 | 'url' => $url, | ||
192 | 'entries' => $entries, | ||
193 | ], | ||
194 | new Response('', 200, ['Content-Type' => 'application/rss+xml']) | ||
195 | ); | ||
116 | } | 196 | } |
117 | } | 197 | } |
diff --git a/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php b/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php new file mode 100644 index 00000000..fa2066dc --- /dev/null +++ b/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php | |||
@@ -0,0 +1,174 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Controller; | ||
4 | |||
5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | ||
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | ||
7 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
8 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Wallabag\CoreBundle\Entity\SiteCredential; | ||
10 | use Wallabag\UserBundle\Entity\User; | ||
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/StaticController.php b/src/Wallabag/CoreBundle/Controller/StaticController.php index 82714217..318af303 100644 --- a/src/Wallabag/CoreBundle/Controller/StaticController.php +++ b/src/Wallabag/CoreBundle/Controller/StaticController.php | |||
@@ -16,7 +16,9 @@ class StaticController extends Controller | |||
16 | 16 | ||
17 | return $this->render( | 17 | return $this->render( |
18 | '@WallabagCore/themes/common/Static/howto.html.twig', | 18 | '@WallabagCore/themes/common/Static/howto.html.twig', |
19 | ['addonsUrl' => $addonsUrl] | 19 | [ |
20 | 'addonsUrl' => $addonsUrl, | ||
21 | ] | ||
20 | ); | 22 | ); |
21 | } | 23 | } |
22 | 24 | ||
@@ -40,8 +42,7 @@ class StaticController extends Controller | |||
40 | public function quickstartAction() | 42 | public function quickstartAction() |
41 | { | 43 | { |
42 | return $this->render( | 44 | return $this->render( |
43 | '@WallabagCore/themes/common/Static/quickstart.html.twig', | 45 | '@WallabagCore/themes/common/Static/quickstart.html.twig' |
44 | [] | ||
45 | ); | 46 | ); |
46 | } | 47 | } |
47 | } | 48 | } |
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index 8a093289..616c37f2 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php | |||
@@ -4,13 +4,13 @@ namespace Wallabag\CoreBundle\Controller; | |||
4 | 4 | ||
5 | use Pagerfanta\Adapter\ArrayAdapter; | 5 | use Pagerfanta\Adapter\ArrayAdapter; |
6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | 6 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; |
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 9 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
9 | use Symfony\Component\HttpFoundation\Request; | 10 | use Symfony\Component\HttpFoundation\Request; |
10 | use Wallabag\CoreBundle\Entity\Entry; | 11 | use Wallabag\CoreBundle\Entity\Entry; |
11 | use Wallabag\CoreBundle\Entity\Tag; | 12 | use Wallabag\CoreBundle\Entity\Tag; |
12 | use Wallabag\CoreBundle\Form\Type\NewTagType; | 13 | use Wallabag\CoreBundle\Form\Type\NewTagType; |
13 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||
14 | 14 | ||
15 | class TagController extends Controller | 15 | class TagController extends Controller |
16 | { | 16 | { |
@@ -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 | ); |
@@ -65,12 +65,12 @@ class TagController extends Controller | |||
65 | $em->flush(); | 65 | $em->flush(); |
66 | 66 | ||
67 | // remove orphan tag in case no entries are associated to it | 67 | // remove orphan tag in case no entries are associated to it |
68 | if (count($tag->getEntries()) === 0) { | 68 | if (0 === count($tag->getEntries())) { |
69 | $em->remove($tag); | 69 | $em->remove($tag); |
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,27 +84,11 @@ class TagController extends Controller | |||
84 | */ | 84 | */ |
85 | public function showTagAction() | 85 | public function showTagAction() |
86 | { | 86 | { |
87 | $tags = $this->getDoctrine() | 87 | $tags = $this->get('wallabag_core.tag_repository') |
88 | ->getRepository('WallabagCoreBundle:Tag') | 88 | ->findAllFlatTagsWithNbEntries($this->getUser()->getId()); |
89 | ->findAllTags($this->getUser()->getId()); | ||
90 | |||
91 | $flatTags = []; | ||
92 | |||
93 | foreach ($tags as $tag) { | ||
94 | $nbEntries = $this->getDoctrine() | ||
95 | ->getRepository('WallabagCoreBundle:Entry') | ||
96 | ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId()); | ||
97 | |||
98 | $flatTags[] = [ | ||
99 | 'id' => $tag->getId(), | ||
100 | 'label' => $tag->getLabel(), | ||
101 | 'slug' => $tag->getSlug(), | ||
102 | 'nbEntries' => $nbEntries, | ||
103 | ]; | ||
104 | } | ||
105 | 89 | ||
106 | return $this->render('WallabagCoreBundle:Tag:tags.html.twig', [ | 90 | return $this->render('WallabagCoreBundle:Tag:tags.html.twig', [ |
107 | 'tags' => $flatTags, | 91 | 'tags' => $tags, |
108 | ]); | 92 | ]); |
109 | } | 93 | } |
110 | 94 | ||
@@ -119,14 +103,14 @@ class TagController extends Controller | |||
119 | */ | 103 | */ |
120 | public function showEntriesForTagAction(Tag $tag, $page, Request $request) | 104 | public function showEntriesForTagAction(Tag $tag, $page, Request $request) |
121 | { | 105 | { |
122 | $entriesByTag = $this->getDoctrine() | 106 | $entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId( |
123 | ->getRepository('WallabagCoreBundle:Entry') | 107 | $this->getUser()->getId(), |
124 | ->findAllByTagId($this->getUser()->getId(), $tag->getId()); | 108 | $tag->getId() |
109 | ); | ||
125 | 110 | ||
126 | $pagerAdapter = new ArrayAdapter($entriesByTag); | 111 | $pagerAdapter = new ArrayAdapter($entriesByTag); |
127 | 112 | ||
128 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') | 113 | $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter); |
129 | ->prepare($pagerAdapter, $page); | ||
130 | 114 | ||
131 | try { | 115 | try { |
132 | $entries->setCurrentPage($page); | 116 | $entries->setCurrentPage($page); |
@@ -143,7 +127,7 @@ class TagController extends Controller | |||
143 | 'form' => null, | 127 | 'form' => null, |
144 | 'entries' => $entries, | 128 | 'entries' => $entries, |
145 | 'currentPage' => $page, | 129 | 'currentPage' => $page, |
146 | 'tag' => $tag->getSlug(), | 130 | 'tag' => $tag, |
147 | ]); | 131 | ]); |
148 | } | 132 | } |
149 | } | 133 | } |
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php index a723656e..3fe88e7f 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php | |||
@@ -2,167 +2,31 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\DataFixtures\ORM; | 3 | namespace Wallabag\CoreBundle\DataFixtures\ORM; |
4 | 4 | ||
5 | use Craue\ConfigBundle\Entity\Setting; | ||
5 | use Doctrine\Common\DataFixtures\AbstractFixture; | 6 | use Doctrine\Common\DataFixtures\AbstractFixture; |
6 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; | 7 | use Doctrine\Common\DataFixtures\OrderedFixtureInterface; |
7 | use Doctrine\Common\Persistence\ObjectManager; | 8 | use Doctrine\Common\Persistence\ObjectManager; |
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/LoadTagData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php index 6de561e0..0ecfd18b 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php | |||
@@ -19,7 +19,7 @@ class LoadTagData extends AbstractFixture implements OrderedFixtureInterface | |||
19 | 19 | ||
20 | $manager->persist($tag1); | 20 | $manager->persist($tag1); |
21 | 21 | ||
22 | $this->addReference('foo-tag', $tag1); | 22 | $this->addReference('foo-bar-tag', $tag1); |
23 | 23 | ||
24 | $tag2 = new Tag(); | 24 | $tag2 = new Tag(); |
25 | $tag2->setLabel('bar'); | 25 | $tag2->setLabel('bar'); |
@@ -35,6 +35,13 @@ class LoadTagData extends AbstractFixture implements OrderedFixtureInterface | |||
35 | 35 | ||
36 | $this->addReference('baz-tag', $tag3); | 36 | $this->addReference('baz-tag', $tag3); |
37 | 37 | ||
38 | $tag4 = new Tag(); | ||
39 | $tag4->setLabel('foo'); | ||
40 | |||
41 | $manager->persist($tag4); | ||
42 | |||
43 | $this->addReference('foo-tag', $tag4); | ||
44 | |||
38 | $manager->flush(); | 45 | $manager->flush(); |
39 | } | 46 | } |
40 | 47 | ||
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..a3ef2b53 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php | |||
@@ -26,8 +26,12 @@ 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'); |
32 | $loader->load('parameters.yml'); | 36 | $loader->load('parameters.yml'); |
33 | } | 37 | } |
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 7276b437..cfb8db75 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php | |||
@@ -5,14 +5,15 @@ namespace Wallabag\CoreBundle\Entity; | |||
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 Hateoas\Configuration\Annotation as Hateoas; | 7 | use Hateoas\Configuration\Annotation as Hateoas; |
8 | use JMS\Serializer\Annotation\Groups; | ||
9 | use JMS\Serializer\Annotation\XmlRoot; | ||
10 | use JMS\Serializer\Annotation\Exclude; | 8 | use JMS\Serializer\Annotation\Exclude; |
11 | use JMS\Serializer\Annotation\VirtualProperty; | 9 | use JMS\Serializer\Annotation\Groups; |
12 | use JMS\Serializer\Annotation\SerializedName; | 10 | use JMS\Serializer\Annotation\SerializedName; |
11 | use JMS\Serializer\Annotation\VirtualProperty; | ||
12 | use JMS\Serializer\Annotation\XmlRoot; | ||
13 | use Symfony\Component\Validator\Constraints as Assert; | 13 | use Symfony\Component\Validator\Constraints as Assert; |
14 | use Wallabag\UserBundle\Entity\User; | ||
15 | use Wallabag\AnnotationBundle\Entity\Annotation; | 14 | use Wallabag\AnnotationBundle\Entity\Annotation; |
15 | use Wallabag\CoreBundle\Helper\EntityTimestampsTrait; | ||
16 | use Wallabag\UserBundle\Entity\User; | ||
16 | 17 | ||
17 | /** | 18 | /** |
18 | * Entry. | 19 | * Entry. |
@@ -32,6 +33,8 @@ use Wallabag\AnnotationBundle\Entity\Annotation; | |||
32 | */ | 33 | */ |
33 | class Entry | 34 | class Entry |
34 | { | 35 | { |
36 | use EntityTimestampsTrait; | ||
37 | |||
35 | /** @Serializer\XmlAttribute */ | 38 | /** @Serializer\XmlAttribute */ |
36 | /** | 39 | /** |
37 | * @var int | 40 | * @var int |
@@ -122,6 +125,33 @@ class Entry | |||
122 | private $updatedAt; | 125 | private $updatedAt; |
123 | 126 | ||
124 | /** | 127 | /** |
128 | * @var \DateTime | ||
129 | * | ||
130 | * @ORM\Column(name="published_at", type="datetime", nullable=true) | ||
131 | * | ||
132 | * @Groups({"entries_for_user", "export_all"}) | ||
133 | */ | ||
134 | private $publishedAt; | ||
135 | |||
136 | /** | ||
137 | * @var array | ||
138 | * | ||
139 | * @ORM\Column(name="published_by", type="array", nullable=true) | ||
140 | * | ||
141 | * @Groups({"entries_for_user", "export_all"}) | ||
142 | */ | ||
143 | private $publishedBy; | ||
144 | |||
145 | /** | ||
146 | * @var \DateTime | ||
147 | * | ||
148 | * @ORM\Column(name="starred_at", type="datetime", nullable=true) | ||
149 | * | ||
150 | * @Groups({"entries_for_user", "export_all"}) | ||
151 | */ | ||
152 | private $starredAt = null; | ||
153 | |||
154 | /** | ||
125 | * @ORM\OneToMany(targetEntity="Wallabag\AnnotationBundle\Entity\Annotation", mappedBy="entry", cascade={"persist", "remove"}) | 155 | * @ORM\OneToMany(targetEntity="Wallabag\AnnotationBundle\Entity\Annotation", mappedBy="entry", cascade={"persist", "remove"}) |
126 | * @ORM\JoinTable | 156 | * @ORM\JoinTable |
127 | * | 157 | * |
@@ -150,11 +180,11 @@ class Entry | |||
150 | /** | 180 | /** |
151 | * @var int | 181 | * @var int |
152 | * | 182 | * |
153 | * @ORM\Column(name="reading_time", type="integer", nullable=true) | 183 | * @ORM\Column(name="reading_time", type="integer", nullable=false) |
154 | * | 184 | * |
155 | * @Groups({"entries_for_user", "export_all"}) | 185 | * @Groups({"entries_for_user", "export_all"}) |
156 | */ | 186 | */ |
157 | private $readingTime; | 187 | private $readingTime = 0; |
158 | 188 | ||
159 | /** | 189 | /** |
160 | * @var string | 190 | * @var string |
@@ -175,22 +205,22 @@ class Entry | |||
175 | private $previewPicture; | 205 | private $previewPicture; |
176 | 206 | ||
177 | /** | 207 | /** |
178 | * @var bool | 208 | * @var string |
179 | * | 209 | * |
180 | * @ORM\Column(name="is_public", type="boolean", nullable=true, options={"default" = false}) | 210 | * @ORM\Column(name="http_status", type="string", length=3, nullable=true) |
181 | * | 211 | * |
182 | * @Groups({"export_all"}) | 212 | * @Groups({"entries_for_user", "export_all"}) |
183 | */ | 213 | */ |
184 | private $isPublic; | 214 | private $httpStatus; |
185 | 215 | ||
186 | /** | 216 | /** |
187 | * @var string | 217 | * @var array |
188 | * | 218 | * |
189 | * @ORM\Column(name="http_status", type="string", length=3, nullable=true) | 219 | * @ORM\Column(name="headers", type="array", nullable=true) |
190 | * | 220 | * |
191 | * @Groups({"entries_for_user", "export_all"}) | 221 | * @Groups({"entries_for_user", "export_all"}) |
192 | */ | 222 | */ |
193 | private $httpStatus; | 223 | private $headers; |
194 | 224 | ||
195 | /** | 225 | /** |
196 | * @Exclude | 226 | * @Exclude |
@@ -455,16 +485,41 @@ class Entry | |||
455 | } | 485 | } |
456 | 486 | ||
457 | /** | 487 | /** |
458 | * @ORM\PrePersist | 488 | * @return \DateTime|null |
459 | * @ORM\PreUpdate | ||
460 | */ | 489 | */ |
461 | public function timestamps() | 490 | public function getStarredAt() |
462 | { | 491 | { |
463 | if (is_null($this->createdAt)) { | 492 | return $this->starredAt; |
464 | $this->createdAt = new \DateTime(); | 493 | } |
494 | |||
495 | /** | ||
496 | * @param \DateTime|null $starredAt | ||
497 | * | ||
498 | * @return Entry | ||
499 | */ | ||
500 | public function setStarredAt($starredAt = null) | ||
501 | { | ||
502 | $this->starredAt = $starredAt; | ||
503 | |||
504 | return $this; | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * update isStarred and starred_at fields. | ||
509 | * | ||
510 | * @param bool $isStarred | ||
511 | * | ||
512 | * @return Entry | ||
513 | */ | ||
514 | public function updateStar($isStarred = false) | ||
515 | { | ||
516 | $this->setStarred($isStarred); | ||
517 | $this->setStarredAt(null); | ||
518 | if ($this->isStarred()) { | ||
519 | $this->setStarredAt(new \DateTime()); | ||
465 | } | 520 | } |
466 | 521 | ||
467 | $this->updatedAt = new \DateTime(); | 522 | return $this; |
468 | } | 523 | } |
469 | 524 | ||
470 | /** | 525 | /** |
@@ -532,23 +587,7 @@ class Entry | |||
532 | } | 587 | } |
533 | 588 | ||
534 | /** | 589 | /** |
535 | * @return bool | 590 | * @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 | */ | 591 | */ |
553 | public function getTags() | 592 | public function getTags() |
554 | { | 593 | { |
@@ -591,6 +630,11 @@ class Entry | |||
591 | $tag->addEntry($this); | 630 | $tag->addEntry($this); |
592 | } | 631 | } |
593 | 632 | ||
633 | /** | ||
634 | * Remove the given tag from the entry (if the tag is associated). | ||
635 | * | ||
636 | * @param Tag $tag | ||
637 | */ | ||
594 | public function removeTag(Tag $tag) | 638 | public function removeTag(Tag $tag) |
595 | { | 639 | { |
596 | if (!$this->tags->contains($tag)) { | 640 | if (!$this->tags->contains($tag)) { |
@@ -602,6 +646,17 @@ class Entry | |||
602 | } | 646 | } |
603 | 647 | ||
604 | /** | 648 | /** |
649 | * Remove all assigned tags from the entry. | ||
650 | */ | ||
651 | public function removeAllTags() | ||
652 | { | ||
653 | foreach ($this->tags as $tag) { | ||
654 | $this->tags->removeElement($tag); | ||
655 | $tag->removeEntry($this); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | /** | ||
605 | * Set previewPicture. | 660 | * Set previewPicture. |
606 | * | 661 | * |
607 | * @param string $previewPicture | 662 | * @param string $previewPicture |
@@ -683,7 +738,22 @@ class Entry | |||
683 | } | 738 | } |
684 | 739 | ||
685 | /** | 740 | /** |
686 | * @return int | 741 | * Used in the entries filter so it's more explicit for the end user than the uid. |
742 | * Also used in the API. | ||
743 | * | ||
744 | * @VirtualProperty | ||
745 | * @SerializedName("is_public") | ||
746 | * @Groups({"entries_for_user"}) | ||
747 | * | ||
748 | * @return bool | ||
749 | */ | ||
750 | public function isPublic() | ||
751 | { | ||
752 | return null !== $this->uid; | ||
753 | } | ||
754 | |||
755 | /** | ||
756 | * @return string | ||
687 | */ | 757 | */ |
688 | public function getHttpStatus() | 758 | public function getHttpStatus() |
689 | { | 759 | { |
@@ -691,7 +761,7 @@ class Entry | |||
691 | } | 761 | } |
692 | 762 | ||
693 | /** | 763 | /** |
694 | * @param int $httpStatus | 764 | * @param string $httpStatus |
695 | * | 765 | * |
696 | * @return Entry | 766 | * @return Entry |
697 | */ | 767 | */ |
@@ -701,4 +771,64 @@ class Entry | |||
701 | 771 | ||
702 | return $this; | 772 | return $this; |
703 | } | 773 | } |
774 | |||
775 | /** | ||
776 | * @return \Datetime | ||
777 | */ | ||
778 | public function getPublishedAt() | ||
779 | { | ||
780 | return $this->publishedAt; | ||
781 | } | ||
782 | |||
783 | /** | ||
784 | * @param \Datetime $publishedAt | ||
785 | * | ||
786 | * @return Entry | ||
787 | */ | ||
788 | public function setPublishedAt(\Datetime $publishedAt) | ||
789 | { | ||
790 | $this->publishedAt = $publishedAt; | ||
791 | |||
792 | return $this; | ||
793 | } | ||
794 | |||
795 | /** | ||
796 | * @return array | ||
797 | */ | ||
798 | public function getPublishedBy() | ||
799 | { | ||
800 | return $this->publishedBy; | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * @param array $publishedBy | ||
805 | * | ||
806 | * @return Entry | ||
807 | */ | ||
808 | public function setPublishedBy($publishedBy) | ||
809 | { | ||
810 | $this->publishedBy = $publishedBy; | ||
811 | |||
812 | return $this; | ||
813 | } | ||
814 | |||
815 | /** | ||
816 | * @return array | ||
817 | */ | ||
818 | public function getHeaders() | ||
819 | { | ||
820 | return $this->headers; | ||
821 | } | ||
822 | |||
823 | /** | ||
824 | * @param array $headers | ||
825 | * | ||
826 | * @return Entry | ||
827 | */ | ||
828 | public function setHeaders($headers) | ||
829 | { | ||
830 | $this->headers = $headers; | ||
831 | |||
832 | return $this; | ||
833 | } | ||
704 | } | 834 | } |
diff --git a/src/Wallabag/CoreBundle/Entity/SiteCredential.php b/src/Wallabag/CoreBundle/Entity/SiteCredential.php new file mode 100644 index 00000000..ac714359 --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/SiteCredential.php | |||
@@ -0,0 +1,188 @@ | |||
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\CoreBundle\Helper\EntityTimestampsTrait; | ||
8 | use Wallabag\UserBundle\Entity\User; | ||
9 | |||
10 | /** | ||
11 | * SiteCredential. | ||
12 | * | ||
13 | * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\SiteCredentialRepository") | ||
14 | * @ORM\Table(name="`site_credential`") | ||
15 | * @ORM\HasLifecycleCallbacks() | ||
16 | */ | ||
17 | class SiteCredential | ||
18 | { | ||
19 | use EntityTimestampsTrait; | ||
20 | |||
21 | /** | ||
22 | * @var int | ||
23 | * | ||
24 | * @ORM\Column(name="id", type="integer") | ||
25 | * @ORM\Id | ||
26 | * @ORM\GeneratedValue(strategy="AUTO") | ||
27 | */ | ||
28 | private $id; | ||
29 | |||
30 | /** | ||
31 | * @var string | ||
32 | * | ||
33 | * @Assert\NotBlank() | ||
34 | * @Assert\Length(max=255) | ||
35 | * @ORM\Column(name="host", type="string", length=255) | ||
36 | */ | ||
37 | private $host; | ||
38 | |||
39 | /** | ||
40 | * @var string | ||
41 | * | ||
42 | * @Assert\NotBlank() | ||
43 | * @ORM\Column(name="username", type="text") | ||
44 | */ | ||
45 | private $username; | ||
46 | |||
47 | /** | ||
48 | * @var string | ||
49 | * | ||
50 | * @Assert\NotBlank() | ||
51 | * @ORM\Column(name="password", type="text") | ||
52 | */ | ||
53 | private $password; | ||
54 | |||
55 | /** | ||
56 | * @var \DateTime | ||
57 | * | ||
58 | * @ORM\Column(name="createdAt", type="datetime") | ||
59 | */ | ||
60 | private $createdAt; | ||
61 | |||
62 | /** | ||
63 | * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="siteCredentials") | ||
64 | */ | ||
65 | private $user; | ||
66 | |||
67 | /* | ||
68 | * @param User $user | ||
69 | */ | ||
70 | public function __construct(User $user) | ||
71 | { | ||
72 | $this->user = $user; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Get id. | ||
77 | * | ||
78 | * @return int | ||
79 | */ | ||
80 | public function getId() | ||
81 | { | ||
82 | return $this->id; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Set host. | ||
87 | * | ||
88 | * @param string $host | ||
89 | * | ||
90 | * @return SiteCredential | ||
91 | */ | ||
92 | public function setHost($host) | ||
93 | { | ||
94 | $this->host = $host; | ||
95 | |||
96 | return $this; | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Get host. | ||
101 | * | ||
102 | * @return string | ||
103 | */ | ||
104 | public function getHost() | ||
105 | { | ||
106 | return $this->host; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * Set username. | ||
111 | * | ||
112 | * @param string $username | ||
113 | * | ||
114 | * @return SiteCredential | ||
115 | */ | ||
116 | public function setUsername($username) | ||
117 | { | ||
118 | $this->username = $username; | ||
119 | |||
120 | return $this; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Get username. | ||
125 | * | ||
126 | * @return string | ||
127 | */ | ||
128 | public function getUsername() | ||
129 | { | ||
130 | return $this->username; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Set password. | ||
135 | * | ||
136 | * @param string $password | ||
137 | * | ||
138 | * @return SiteCredential | ||
139 | */ | ||
140 | public function setPassword($password) | ||
141 | { | ||
142 | $this->password = $password; | ||
143 | |||
144 | return $this; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * Get password. | ||
149 | * | ||
150 | * @return string | ||
151 | */ | ||
152 | public function getPassword() | ||
153 | { | ||
154 | return $this->password; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * Set createdAt. | ||
159 | * | ||
160 | * @param \DateTime $createdAt | ||
161 | * | ||
162 | * @return SiteCredential | ||
163 | */ | ||
164 | public function setCreatedAt($createdAt) | ||
165 | { | ||
166 | $this->createdAt = $createdAt; | ||
167 | |||
168 | return $this; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * Get createdAt. | ||
173 | * | ||
174 | * @return \DateTime | ||
175 | */ | ||
176 | public function getCreatedAt() | ||
177 | { | ||
178 | return $this->createdAt; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * @return User | ||
183 | */ | ||
184 | public function getUser() | ||
185 | { | ||
186 | return $this->user; | ||
187 | } | ||
188 | } | ||
diff --git a/src/Wallabag/CoreBundle/Entity/Tag.php b/src/Wallabag/CoreBundle/Entity/Tag.php index 4b480ff1..a6dc8c50 100644 --- a/src/Wallabag/CoreBundle/Entity/Tag.php +++ b/src/Wallabag/CoreBundle/Entity/Tag.php | |||
@@ -4,9 +4,9 @@ namespace Wallabag\CoreBundle\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 Gedmo\Mapping\Annotation as Gedmo; | ||
7 | use JMS\Serializer\Annotation\ExclusionPolicy; | 8 | use JMS\Serializer\Annotation\ExclusionPolicy; |
8 | use JMS\Serializer\Annotation\Expose; | 9 | use JMS\Serializer\Annotation\Expose; |
9 | use Gedmo\Mapping\Annotation as Gedmo; | ||
10 | use JMS\Serializer\Annotation\XmlRoot; | 10 | use JMS\Serializer\Annotation\XmlRoot; |
11 | 11 | ||
12 | /** | 12 | /** |
@@ -78,7 +78,7 @@ class Tag | |||
78 | */ | 78 | */ |
79 | public function setLabel($label) | 79 | public function setLabel($label) |
80 | { | 80 | { |
81 | $this->label = $label; | 81 | $this->label = mb_convert_case($label, MB_CASE_LOWER); |
82 | 82 | ||
83 | return $this; | 83 | return $this; |
84 | } | 84 | } |
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/DownloadImagesSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php index 4ebe837b..1dd0a1a4 100644 --- a/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php +++ b/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php | |||
@@ -2,13 +2,13 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Event\Subscriber; | 3 | namespace Wallabag\CoreBundle\Event\Subscriber; |
4 | 4 | ||
5 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; | 5 | use Doctrine\ORM\EntityManager; |
6 | use Psr\Log\LoggerInterface; | 6 | use Psr\Log\LoggerInterface; |
7 | use Wallabag\CoreBundle\Helper\DownloadImages; | 7 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
8 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Wallabag\CoreBundle\Entity\Entry; |
9 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
10 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | 9 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; |
11 | use Doctrine\ORM\EntityManager; | 10 | use Wallabag\CoreBundle\Event\EntrySavedEvent; |
11 | use Wallabag\CoreBundle\Helper\DownloadImages; | ||
12 | 12 | ||
13 | class DownloadImagesSubscriber implements EventSubscriberInterface | 13 | class DownloadImagesSubscriber implements EventSubscriberInterface |
14 | { | 14 | { |
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php index 3b4c4cf9..9c1d8a1d 100644 --- a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php +++ b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php | |||
@@ -2,10 +2,10 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Event\Subscriber; | 3 | namespace Wallabag\CoreBundle\Event\Subscriber; |
4 | 4 | ||
5 | use Doctrine\Bundle\DoctrineBundle\Registry; | ||
5 | use Doctrine\Common\EventSubscriber; | 6 | use Doctrine\Common\EventSubscriber; |
6 | use Doctrine\ORM\Event\LifecycleEventArgs; | 7 | use Doctrine\ORM\Event\LifecycleEventArgs; |
7 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Wallabag\CoreBundle\Entity\Entry; |
8 | use Doctrine\Bundle\DoctrineBundle\Registry; | ||
9 | 9 | ||
10 | /** | 10 | /** |
11 | * SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry. | 11 | * SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry. |
@@ -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/Event/Subscriber/TablePrefixSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php index 711c3bf8..fb8f225f 100644 --- a/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php +++ b/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php | |||
@@ -39,12 +39,12 @@ class TablePrefixSubscriber implements EventSubscriber | |||
39 | return; | 39 | return; |
40 | } | 40 | } |
41 | 41 | ||
42 | $classMetadata->setPrimaryTable(['name' => $this->prefix.$classMetadata->getTableName()]); | 42 | $classMetadata->setPrimaryTable(['name' => $this->prefix . $classMetadata->getTableName()]); |
43 | 43 | ||
44 | foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { | 44 | foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { |
45 | if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) { | 45 | if (ClassMetadataInfo::MANY_TO_MANY === $mapping['type'] && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) { |
46 | $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; | 46 | $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; |
47 | $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix.$mappedTableName; | 47 | $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; |
48 | } | 48 | } |
49 | } | 49 | } |
50 | } | 50 | } |
diff --git a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php index cb4bee83..57dbc95e 100644 --- a/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php +++ b/src/Wallabag/CoreBundle/Form/DataTransformer/StringToListTransformer.php | |||
@@ -48,7 +48,7 @@ class StringToListTransformer implements DataTransformerInterface | |||
48 | */ | 48 | */ |
49 | public function reverseTransform($string) | 49 | public function reverseTransform($string) |
50 | { | 50 | { |
51 | if ($string === null) { | 51 | if (null === $string) { |
52 | return; | 52 | return; |
53 | } | 53 | } |
54 | 54 | ||
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..6f8c9e27 100644 --- a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php +++ b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php | |||
@@ -4,12 +4,12 @@ namespace Wallabag\CoreBundle\Form\Type; | |||
4 | 4 | ||
5 | use Doctrine\ORM\EntityRepository; | 5 | use Doctrine\ORM\EntityRepository; |
6 | use Lexik\Bundle\FormFilterBundle\Filter\FilterOperands; | 6 | use Lexik\Bundle\FormFilterBundle\Filter\FilterOperands; |
7 | use Lexik\Bundle\FormFilterBundle\Filter\Query\QueryInterface; | ||
8 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\NumberRangeFilterType; | ||
9 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\DateRangeFilterType; | ||
10 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\TextFilterType; | ||
11 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\CheckboxFilterType; | 7 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\CheckboxFilterType; |
12 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType; | 8 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType; |
9 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\DateRangeFilterType; | ||
10 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\NumberRangeFilterType; | ||
11 | use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\TextFilterType; | ||
12 | use Lexik\Bundle\FormFilterBundle\Filter\Query\QueryInterface; | ||
13 | use Symfony\Component\Form\AbstractType; | 13 | use Symfony\Component\Form\AbstractType; |
14 | use Symfony\Component\Form\FormBuilderInterface; | 14 | use Symfony\Component\Form\FormBuilderInterface; |
15 | use Symfony\Component\HttpFoundation\Response; | 15 | use Symfony\Component\HttpFoundation\Response; |
@@ -99,7 +99,7 @@ class EntryFilterType extends AbstractType | |||
99 | if (strlen($value) <= 2 || empty($value)) { | 99 | if (strlen($value) <= 2 || empty($value)) { |
100 | return; | 100 | return; |
101 | } | 101 | } |
102 | $expression = $filterQuery->getExpr()->like($field, $filterQuery->getExpr()->lower($filterQuery->getExpr()->literal('%'.$value.'%'))); | 102 | $expression = $filterQuery->getExpr()->like($field, $filterQuery->getExpr()->lower($filterQuery->getExpr()->literal('%' . $value . '%'))); |
103 | 103 | ||
104 | return $filterQuery->createCondition($expression); | 104 | return $filterQuery->createCondition($expression); |
105 | }, | 105 | }, |
@@ -113,8 +113,8 @@ class EntryFilterType extends AbstractType | |||
113 | } | 113 | } |
114 | 114 | ||
115 | $paramName = sprintf('%s', str_replace('.', '_', $field)); | 115 | $paramName = sprintf('%s', str_replace('.', '_', $field)); |
116 | $expression = $filterQuery->getExpr()->eq($field, ':'.$paramName); | 116 | $expression = $filterQuery->getExpr()->eq($field, ':' . $paramName); |
117 | $parameters = array($paramName => $value); | 117 | $parameters = [$paramName => $value]; |
118 | 118 | ||
119 | return $filterQuery->createCondition($expression, $parameters); | 119 | return $filterQuery->createCondition($expression, $parameters); |
120 | }, | 120 | }, |
@@ -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..2c85da62 100644 --- a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php +++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php | |||
@@ -5,48 +5,74 @@ 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 Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; | ||
10 | use Wallabag\CoreBundle\Repository\SiteCredentialRepository; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
9 | 12 | ||
10 | class GrabySiteConfigBuilder implements SiteConfigBuilder | 13 | class GrabySiteConfigBuilder implements SiteConfigBuilder |
11 | { | 14 | { |
12 | /** | 15 | /** |
13 | * @var \Graby\SiteConfig\ConfigBuilder | 16 | * @var ConfigBuilder |
14 | */ | 17 | */ |
15 | private $grabyConfigBuilder; | 18 | private $grabyConfigBuilder; |
19 | |||
20 | /** | ||
21 | * @var SiteCredentialRepository | ||
22 | */ | ||
23 | private $credentialRepository; | ||
24 | |||
16 | /** | 25 | /** |
17 | * @var array | 26 | * @var LoggerInterface |
18 | */ | 27 | */ |
19 | private $credentials; | 28 | private $logger; |
29 | |||
30 | /** | ||
31 | * @var User|null | ||
32 | */ | ||
33 | private $currentUser; | ||
20 | 34 | ||
21 | /** | 35 | /** |
22 | * GrabySiteConfigBuilder constructor. | 36 | * GrabySiteConfigBuilder constructor. |
23 | * | 37 | * |
24 | * @param \Graby\SiteConfig\ConfigBuilder $grabyConfigBuilder | 38 | * @param ConfigBuilder $grabyConfigBuilder |
25 | * @param array $credentials | 39 | * @param TokenStorage $token |
40 | * @param SiteCredentialRepository $credentialRepository | ||
41 | * @param LoggerInterface $logger | ||
26 | */ | 42 | */ |
27 | public function __construct(ConfigBuilder $grabyConfigBuilder, array $credentials = []) | 43 | public function __construct(ConfigBuilder $grabyConfigBuilder, TokenStorage $token, SiteCredentialRepository $credentialRepository, LoggerInterface $logger) |
28 | { | 44 | { |
29 | $this->grabyConfigBuilder = $grabyConfigBuilder; | 45 | $this->grabyConfigBuilder = $grabyConfigBuilder; |
30 | $this->credentials = $credentials; | 46 | $this->credentialRepository = $credentialRepository; |
47 | $this->logger = $logger; | ||
48 | |||
49 | if ($token->getToken()) { | ||
50 | $this->currentUser = $token->getToken()->getUser(); | ||
51 | } | ||
31 | } | 52 | } |
32 | 53 | ||
33 | /** | 54 | /** |
34 | * Builds the SiteConfig for a host. | 55 | * {@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 | */ | 56 | */ |
42 | public function buildForHost($host) | 57 | public function buildForHost($host) |
43 | { | 58 | { |
44 | // required by credentials below | 59 | // required by credentials below |
45 | $host = strtolower($host); | 60 | $host = strtolower($host); |
46 | if (substr($host, 0, 4) == 'www.') { | 61 | if ('www.' === substr($host, 0, 4)) { |
47 | $host = substr($host, 4); | 62 | $host = substr($host, 4); |
48 | } | 63 | } |
49 | 64 | ||
65 | $credentials = null; | ||
66 | if ($this->currentUser) { | ||
67 | $credentials = $this->credentialRepository->findOneByHostAndUser($host, $this->currentUser->getId()); | ||
68 | } | ||
69 | |||
70 | if (null === $credentials) { | ||
71 | $this->logger->debug('Auth: no credentials available for host.', ['host' => $host]); | ||
72 | |||
73 | return false; | ||
74 | } | ||
75 | |||
50 | $config = $this->grabyConfigBuilder->buildForHost($host); | 76 | $config = $this->grabyConfigBuilder->buildForHost($host); |
51 | $parameters = [ | 77 | $parameters = [ |
52 | 'host' => $host, | 78 | 'host' => $host, |
@@ -54,15 +80,47 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder | |||
54 | 'loginUri' => $config->login_uri ?: null, | 80 | 'loginUri' => $config->login_uri ?: null, |
55 | 'usernameField' => $config->login_username_field ?: null, | 81 | 'usernameField' => $config->login_username_field ?: null, |
56 | 'passwordField' => $config->login_password_field ?: null, | 82 | 'passwordField' => $config->login_password_field ?: null, |
57 | 'extraFields' => is_array($config->login_extra_fields) ? $config->login_extra_fields : [], | 83 | 'extraFields' => $this->processExtraFields($config->login_extra_fields), |
58 | 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null, | 84 | 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null, |
85 | 'username' => $credentials['username'], | ||
86 | 'password' => $credentials['password'], | ||
59 | ]; | 87 | ]; |
60 | 88 | ||
61 | if (isset($this->credentials[$host])) { | 89 | $config = new SiteConfig($parameters); |
62 | $parameters['username'] = $this->credentials[$host]['username']; | 90 | |
63 | $parameters['password'] = $this->credentials[$host]['password']; | 91 | // do not leak usernames and passwords in log |
92 | $parameters['username'] = '**masked**'; | ||
93 | $parameters['password'] = '**masked**'; | ||
94 | |||
95 | $this->logger->debug('Auth: add parameters.', ['host' => $host, 'parameters' => $parameters]); | ||
96 | |||
97 | return $config; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Processes login_extra_fields config, transforming an '=' separated array of strings | ||
102 | * into a key/value array. | ||
103 | * | ||
104 | * @param array|mixed $extraFieldsStrings | ||
105 | * | ||
106 | * @return array | ||
107 | */ | ||
108 | protected function processExtraFields($extraFieldsStrings) | ||
109 | { | ||
110 | if (!is_array($extraFieldsStrings)) { | ||
111 | return []; | ||
112 | } | ||
113 | |||
114 | $extraFields = []; | ||
115 | foreach ($extraFieldsStrings as $extraField) { | ||
116 | if (false === strpos($extraField, '=')) { | ||
117 | continue; | ||
118 | } | ||
119 | |||
120 | list($fieldName, $fieldValue) = explode('=', $extraField, 2); | ||
121 | $extraFields[$fieldName] = $fieldValue; | ||
64 | } | 122 | } |
65 | 123 | ||
66 | return new SiteConfig($parameters); | 124 | return $extraFields; |
67 | } | 125 | } |
68 | } | 126 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index f222dd88..854acb6a 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php | |||
@@ -4,11 +4,12 @@ namespace Wallabag\CoreBundle\Helper; | |||
4 | 4 | ||
5 | use Graby\Graby; | 5 | use Graby\Graby; |
6 | use Psr\Log\LoggerInterface; | 6 | use Psr\Log\LoggerInterface; |
7 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; | ||
8 | use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint; | ||
9 | use Symfony\Component\Validator\Constraints\Url as UrlConstraint; | ||
10 | use Symfony\Component\Validator\Validator\ValidatorInterface; | ||
7 | use Wallabag\CoreBundle\Entity\Entry; | 11 | use Wallabag\CoreBundle\Entity\Entry; |
8 | use Wallabag\CoreBundle\Entity\Tag; | ||
9 | use Wallabag\CoreBundle\Tools\Utils; | 12 | use Wallabag\CoreBundle\Tools\Utils; |
10 | use Wallabag\CoreBundle\Repository\TagRepository; | ||
11 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; | ||
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 | * We can also force the content, in case of an import from the v1 for example, so the function won't | ||
41 | * fetch the content from the website but rather use information given with the $content parameter. | ||
42 | * | 40 | * |
43 | * @param Entry $entry Entry to update | 41 | * @param Entry $entry Entry to update |
44 | * @param string $url Url to grab content for | 42 | * @param string $url Url of the content |
45 | * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url | 43 | * @param array $content Array with content provided for import with AT LEAST keys title, html, url to skip the fetchContent from the url |
46 | * | 44 | * @param bool $disableContentUpdate Whether to skip trying to fetch content using Graby |
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,107 +59,169 @@ class ContentProxy | |||
59 | } | 59 | } |
60 | } | 60 | } |
61 | 61 | ||
62 | $title = $content['title']; | 62 | // be sure to keep the url in case of error |
63 | if (!$title && isset($content['open_graph']['og_title'])) { | 63 | // so we'll be able to refetch it in the future |
64 | $title = $content['open_graph']['og_title']; | 64 | $content['url'] = !empty($content['url']) ? $content['url'] : $url; |
65 | } | ||
66 | 65 | ||
67 | $html = $content['html']; | 66 | $this->stockEntry($entry, $content); |
68 | if (false === $html) { | 67 | } |
69 | $html = $this->fetchingErrorMessage; | ||
70 | 68 | ||
71 | if (isset($content['open_graph']['og_description'])) { | 69 | /** |
72 | $html .= '<p><i>But we found a short description: </i></p>'; | 70 | * Use a Symfony validator to ensure the language is well formatted. |
73 | $html .= $content['open_graph']['og_description']; | 71 | * |
74 | } | 72 | * @param Entry $entry |
75 | } | 73 | * @param string $value Language to validate and save |
74 | */ | ||
75 | public function updateLanguage(Entry $entry, $value) | ||
76 | { | ||
77 | // some lang are defined as fr-FR, es-ES. | ||
78 | // replacing - by _ might increase language support | ||
79 | $value = str_replace('-', '_', $value); | ||
76 | 80 | ||
77 | $entry->setUrl($content['url'] ?: $url); | 81 | $errors = $this->validator->validate( |
78 | $entry->setTitle($title); | 82 | $value, |
79 | $entry->setContent($html); | 83 | (new LocaleConstraint()) |
80 | $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); | 84 | ); |
81 | 85 | ||
82 | $entry->setLanguage(isset($content['language']) ? $content['language'] : ''); | 86 | if (0 === count($errors)) { |
83 | $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : ''); | 87 | $entry->setLanguage($value); |
84 | $entry->setReadingTime(Utils::getReadingTime($html)); | ||
85 | 88 | ||
86 | $domainName = parse_url($entry->getUrl(), PHP_URL_HOST); | 89 | return; |
87 | if (false !== $domainName) { | ||
88 | $entry->setDomainName($domainName); | ||
89 | } | 90 | } |
90 | 91 | ||
91 | if (isset($content['open_graph']['og_image']) && $content['open_graph']['og_image']) { | 92 | $this->logger->warning('Language validation failed. ' . (string) $errors); |
92 | $entry->setPreviewPicture($content['open_graph']['og_image']); | 93 | } |
94 | |||
95 | /** | ||
96 | * Use a Symfony validator to ensure the preview picture is a real url. | ||
97 | * | ||
98 | * @param Entry $entry | ||
99 | * @param string $value URL to validate and save | ||
100 | */ | ||
101 | public function updatePreviewPicture(Entry $entry, $value) | ||
102 | { | ||
103 | $errors = $this->validator->validate( | ||
104 | $value, | ||
105 | (new UrlConstraint()) | ||
106 | ); | ||
107 | |||
108 | if (0 === count($errors)) { | ||
109 | $entry->setPreviewPicture($value); | ||
110 | |||
111 | return; | ||
93 | } | 112 | } |
94 | 113 | ||
95 | // if content is an image define as a preview too | 114 | $this->logger->warning('PreviewPicture validation failed. ' . (string) $errors); |
96 | if (isset($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { | 115 | } |
97 | $entry->setPreviewPicture($content['url']); | 116 | |
117 | /** | ||
118 | * Update date. | ||
119 | * | ||
120 | * @param Entry $entry | ||
121 | * @param string $value Date to validate and save | ||
122 | */ | ||
123 | public function updatePublishedAt(Entry $entry, $value) | ||
124 | { | ||
125 | $date = $value; | ||
126 | |||
127 | // is it a timestamp? | ||
128 | if (false !== filter_var($date, FILTER_VALIDATE_INT)) { | ||
129 | $date = '@' . $date; | ||
98 | } | 130 | } |
99 | 131 | ||
100 | try { | 132 | try { |
101 | $this->tagger->tag($entry); | 133 | // is it already a DateTime? |
134 | // (it's inside the try/catch in case of fail to be parse time string) | ||
135 | if (!$date instanceof \DateTime) { | ||
136 | $date = new \DateTime($date); | ||
137 | } | ||
138 | |||
139 | $entry->setPublishedAt($date); | ||
102 | } catch (\Exception $e) { | 140 | } catch (\Exception $e) { |
103 | $this->logger->error('Error while trying to automatically tag an entry.', [ | 141 | $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $entry->getUrl(), 'date' => $value]); |
104 | 'entry_url' => $url, | ||
105 | 'error_msg' => $e->getMessage(), | ||
106 | ]); | ||
107 | } | 142 | } |
108 | |||
109 | return $entry; | ||
110 | } | 143 | } |
111 | 144 | ||
112 | /** | 145 | /** |
113 | * Assign some tags to an entry. | 146 | * Stock entry with fetched or imported content. |
147 | * Will fall back to OpenGraph data if available. | ||
114 | * | 148 | * |
115 | * @param Entry $entry | 149 | * @param Entry $entry Entry to stock |
116 | * @param array|string $tags An array of tag or a string coma separated of tag | 150 | * @param array $content Array with at least title, url & html |
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 | */ | 151 | */ |
120 | public function assignTagsToEntry(Entry $entry, $tags, array $entitiesReady = []) | 152 | private function stockEntry(Entry $entry, array $content) |
121 | { | 153 | { |
122 | if (!is_array($tags)) { | 154 | $entry->setUrl($content['url']); |
123 | $tags = explode(',', $tags); | 155 | |
156 | $domainName = parse_url($entry->getUrl(), PHP_URL_HOST); | ||
157 | if (false !== $domainName) { | ||
158 | $entry->setDomainName($domainName); | ||
124 | } | 159 | } |
125 | 160 | ||
126 | // keeps only Tag entity from the "not yet flushed entities" | 161 | if (!empty($content['title'])) { |
127 | $tagsNotYetFlushed = []; | 162 | $entry->setTitle($content['title']); |
128 | foreach ($entitiesReady as $entity) { | 163 | } elseif (!empty($content['open_graph']['og_title'])) { |
129 | if ($entity instanceof Tag) { | 164 | $entry->setTitle($content['open_graph']['og_title']); |
130 | $tagsNotYetFlushed[$entity->getLabel()] = $entity; | ||
131 | } | ||
132 | } | 165 | } |
133 | 166 | ||
134 | foreach ($tags as $label) { | 167 | $html = $content['html']; |
135 | $label = trim($label); | 168 | if (false === $html) { |
169 | $html = $this->fetchingErrorMessage; | ||
136 | 170 | ||
137 | // avoid empty tag | 171 | if (!empty($content['open_graph']['og_description'])) { |
138 | if (0 === strlen($label)) { | 172 | $html .= '<p><i>But we found a short description: </i></p>'; |
139 | continue; | 173 | $html .= $content['open_graph']['og_description']; |
140 | } | 174 | } |
175 | } | ||
141 | 176 | ||
142 | if (isset($tagsNotYetFlushed[$label])) { | 177 | $entry->setContent($html); |
143 | $tagEntity = $tagsNotYetFlushed[$label]; | 178 | $entry->setReadingTime(Utils::getReadingTime($html)); |
144 | } else { | ||
145 | $tagEntity = $this->tagRepository->findOneByLabel($label); | ||
146 | 179 | ||
147 | if (is_null($tagEntity)) { | 180 | if (!empty($content['status'])) { |
148 | $tagEntity = new Tag(); | 181 | $entry->setHttpStatus($content['status']); |
149 | $tagEntity->setLabel($label); | 182 | } |
150 | } | ||
151 | } | ||
152 | 183 | ||
153 | // only add the tag on the entry if the relation doesn't exist | 184 | if (!empty($content['authors']) && is_array($content['authors'])) { |
154 | if (false === $entry->getTags()->contains($tagEntity)) { | 185 | $entry->setPublishedBy($content['authors']); |
155 | $entry->addTag($tagEntity); | 186 | } |
156 | } | 187 | |
188 | if (!empty($content['all_headers'])) { | ||
189 | $entry->setHeaders($content['all_headers']); | ||
190 | } | ||
191 | |||
192 | if (!empty($content['date'])) { | ||
193 | $this->updatePublishedAt($entry, $content['date']); | ||
194 | } | ||
195 | |||
196 | if (!empty($content['language'])) { | ||
197 | $this->updateLanguage($entry, $content['language']); | ||
198 | } | ||
199 | |||
200 | if (!empty($content['open_graph']['og_image'])) { | ||
201 | $this->updatePreviewPicture($entry, $content['open_graph']['og_image']); | ||
202 | } | ||
203 | |||
204 | // if content is an image, define it as a preview too | ||
205 | if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { | ||
206 | $this->updatePreviewPicture($entry, $content['url']); | ||
207 | } | ||
208 | |||
209 | if (!empty($content['content_type'])) { | ||
210 | $entry->setMimetype($content['content_type']); | ||
211 | } | ||
212 | |||
213 | try { | ||
214 | $this->tagger->tag($entry); | ||
215 | } catch (\Exception $e) { | ||
216 | $this->logger->error('Error while trying to automatically tag an entry.', [ | ||
217 | 'entry_url' => $content['url'], | ||
218 | 'error_msg' => $e->getMessage(), | ||
219 | ]); | ||
157 | } | 220 | } |
158 | } | 221 | } |
159 | 222 | ||
160 | /** | 223 | /** |
161 | * Validate that the given content as enough value to be used | 224 | * Validate that the given content has at least a title, an html and a url. |
162 | * instead of fetch the content from the url. | ||
163 | * | 225 | * |
164 | * @param array $content | 226 | * @param array $content |
165 | * | 227 | * |
@@ -167,6 +229,6 @@ class ContentProxy | |||
167 | */ | 229 | */ |
168 | private function validateContent(array $content) | 230 | private function validateContent(array $content) |
169 | { | 231 | { |
170 | return isset($content['title']) && isset($content['html']) && isset($content['url']) && isset($content['language']) && isset($content['content_type']); | 232 | return !empty($content['title']) && !empty($content['html']) && !empty($content['url']); |
171 | } | 233 | } |
172 | } | 234 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/CryptoProxy.php b/src/Wallabag/CoreBundle/Helper/CryptoProxy.php new file mode 100644 index 00000000..7d8c9888 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/CryptoProxy.php | |||
@@ -0,0 +1,86 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Helper; | ||
4 | |||
5 | use Defuse\Crypto\Crypto; | ||
6 | use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException; | ||
7 | use Defuse\Crypto\Key; | ||
8 | use Psr\Log\LoggerInterface; | ||
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/DetectActiveTheme.php b/src/Wallabag/CoreBundle/Helper/DetectActiveTheme.php index 23e98042..9f90ee3e 100644 --- a/src/Wallabag/CoreBundle/Helper/DetectActiveTheme.php +++ b/src/Wallabag/CoreBundle/Helper/DetectActiveTheme.php | |||
@@ -44,7 +44,7 @@ class DetectActiveTheme implements DeviceDetectionInterface | |||
44 | { | 44 | { |
45 | $token = $this->tokenStorage->getToken(); | 45 | $token = $this->tokenStorage->getToken(); |
46 | 46 | ||
47 | if (is_null($token)) { | 47 | if (null === $token) { |
48 | return $this->defaultTheme; | 48 | return $this->defaultTheme; |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php index 0d330d2a..252ba57c 100644 --- a/src/Wallabag/CoreBundle/Helper/DownloadImages.php +++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php | |||
@@ -2,11 +2,12 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Helper; | 3 | namespace Wallabag\CoreBundle\Helper; |
4 | 4 | ||
5 | use GuzzleHttp\Client; | ||
6 | use GuzzleHttp\Message\Response; | ||
5 | use Psr\Log\LoggerInterface; | 7 | use Psr\Log\LoggerInterface; |
6 | use Symfony\Component\DomCrawler\Crawler; | 8 | use Symfony\Component\DomCrawler\Crawler; |
7 | use GuzzleHttp\Client; | ||
8 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; | ||
9 | use Symfony\Component\Finder\Finder; | 9 | use Symfony\Component\Finder\Finder; |
10 | use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; | ||
10 | 11 | ||
11 | class DownloadImages | 12 | class DownloadImages |
12 | { | 13 | { |
@@ -30,17 +31,6 @@ class DownloadImages | |||
30 | } | 31 | } |
31 | 32 | ||
32 | /** | 33 | /** |
33 | * Setup base folder where all images are going to be saved. | ||
34 | */ | ||
35 | private function setFolder() | ||
36 | { | ||
37 | // if folder doesn't exist, attempt to create one and store the folder name in property $folder | ||
38 | if (!file_exists($this->baseFolder)) { | ||
39 | mkdir($this->baseFolder, 0755, true); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Process the html and extract image from it, save them to local and return the updated html. | 34 | * Process the html and extract image from it, save them to local and return the updated html. |
45 | * | 35 | * |
46 | * @param int $entryId ID of the entry | 36 | * @param int $entryId ID of the entry |
@@ -54,7 +44,7 @@ class DownloadImages | |||
54 | $crawler = new Crawler($html); | 44 | $crawler = new Crawler($html); |
55 | $result = $crawler | 45 | $result = $crawler |
56 | ->filterXpath('//img') | 46 | ->filterXpath('//img') |
57 | ->extract(array('src')); | 47 | ->extract(['src']); |
58 | 48 | ||
59 | $relativePath = $this->getRelativePath($entryId); | 49 | $relativePath = $this->getRelativePath($entryId); |
60 | 50 | ||
@@ -66,6 +56,11 @@ class DownloadImages | |||
66 | continue; | 56 | continue; |
67 | } | 57 | } |
68 | 58 | ||
59 | // if image contains "&" and we can't find it in the html it might be because it's encoded as & | ||
60 | if (false !== stripos($image, '&') && false === stripos($html, $image)) { | ||
61 | $image = str_replace('&', '&', $image); | ||
62 | } | ||
63 | |||
69 | $html = str_replace($image, $imagePath, $html); | 64 | $html = str_replace($image, $imagePath, $html); |
70 | } | 65 | } |
71 | 66 | ||
@@ -91,9 +86,9 @@ class DownloadImages | |||
91 | $relativePath = $this->getRelativePath($entryId); | 86 | $relativePath = $this->getRelativePath($entryId); |
92 | } | 87 | } |
93 | 88 | ||
94 | $this->logger->debug('DownloadImages: working on image: '.$imagePath); | 89 | $this->logger->debug('DownloadImages: working on image: ' . $imagePath); |
95 | 90 | ||
96 | $folderPath = $this->baseFolder.'/'.$relativePath; | 91 | $folderPath = $this->baseFolder . '/' . $relativePath; |
97 | 92 | ||
98 | // build image path | 93 | // build image path |
99 | $absolutePath = $this->getAbsoluteLink($url, $imagePath); | 94 | $absolutePath = $this->getAbsoluteLink($url, $imagePath); |
@@ -111,15 +106,13 @@ class DownloadImages | |||
111 | return false; | 106 | return false; |
112 | } | 107 | } |
113 | 108 | ||
114 | $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); | 109 | $ext = $this->getExtensionFromResponse($res, $imagePath); |
115 | $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); | 110 | 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; | 111 | return false; |
120 | } | 112 | } |
113 | |||
121 | $hashImage = hash('crc32', $absolutePath); | 114 | $hashImage = hash('crc32', $absolutePath); |
122 | $localPath = $folderPath.'/'.$hashImage.'.'.$ext; | 115 | $localPath = $folderPath . '/' . $hashImage . '.' . $ext; |
123 | 116 | ||
124 | try { | 117 | try { |
125 | $im = imagecreatefromstring($res->getBody()); | 118 | $im = imagecreatefromstring($res->getBody()); |
@@ -152,7 +145,7 @@ class DownloadImages | |||
152 | 145 | ||
153 | imagedestroy($im); | 146 | imagedestroy($im); |
154 | 147 | ||
155 | return $this->wallabagUrl.'/assets/images/'.$relativePath.'/'.$hashImage.'.'.$ext; | 148 | return $this->wallabagUrl . '/assets/images/' . $relativePath . '/' . $hashImage . '.' . $ext; |
156 | } | 149 | } |
157 | 150 | ||
158 | /** | 151 | /** |
@@ -163,7 +156,7 @@ class DownloadImages | |||
163 | public function removeImages($entryId) | 156 | public function removeImages($entryId) |
164 | { | 157 | { |
165 | $relativePath = $this->getRelativePath($entryId); | 158 | $relativePath = $this->getRelativePath($entryId); |
166 | $folderPath = $this->baseFolder.'/'.$relativePath; | 159 | $folderPath = $this->baseFolder . '/' . $relativePath; |
167 | 160 | ||
168 | $finder = new Finder(); | 161 | $finder = new Finder(); |
169 | $finder | 162 | $finder |
@@ -179,6 +172,17 @@ class DownloadImages | |||
179 | } | 172 | } |
180 | 173 | ||
181 | /** | 174 | /** |
175 | * Setup base folder where all images are going to be saved. | ||
176 | */ | ||
177 | private function setFolder() | ||
178 | { | ||
179 | // if folder doesn't exist, attempt to create one and store the folder name in property $folder | ||
180 | if (!file_exists($this->baseFolder)) { | ||
181 | mkdir($this->baseFolder, 0755, true); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | /** | ||
182 | * Generate the folder where we are going to save images based on the entry url. | 186 | * Generate the folder where we are going to save images based on the entry url. |
183 | * | 187 | * |
184 | * @param int $entryId ID of the entry | 188 | * @param int $entryId ID of the entry |
@@ -188,8 +192,8 @@ class DownloadImages | |||
188 | private function getRelativePath($entryId) | 192 | private function getRelativePath($entryId) |
189 | { | 193 | { |
190 | $hashId = hash('crc32', $entryId); | 194 | $hashId = hash('crc32', $entryId); |
191 | $relativePath = $hashId[0].'/'.$hashId[1].'/'.$hashId; | 195 | $relativePath = $hashId[0] . '/' . $hashId[1] . '/' . $hashId; |
192 | $folderPath = $this->baseFolder.'/'.$relativePath; | 196 | $folderPath = $this->baseFolder . '/' . $relativePath; |
193 | 197 | ||
194 | if (!file_exists($folderPath)) { | 198 | if (!file_exists($folderPath)) { |
195 | mkdir($folderPath, 0777, true); | 199 | mkdir($folderPath, 0777, true); |
@@ -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/EntityTimestampsTrait.php b/src/Wallabag/CoreBundle/Helper/EntityTimestampsTrait.php new file mode 100644 index 00000000..1b1ff54a --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/EntityTimestampsTrait.php | |||
@@ -0,0 +1,24 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Helper; | ||
4 | |||
5 | use Doctrine\ORM\Mapping as ORM; | ||
6 | |||
7 | /** | ||
8 | * Trait to handle created & updated date of an Entity. | ||
9 | */ | ||
10 | trait EntityTimestampsTrait | ||
11 | { | ||
12 | /** | ||
13 | * @ORM\PrePersist | ||
14 | * @ORM\PreUpdate | ||
15 | */ | ||
16 | public function timestamps() | ||
17 | { | ||
18 | if (null === $this->createdAt) { | ||
19 | $this->createdAt = new \DateTime(); | ||
20 | } | ||
21 | |||
22 | $this->updatedAt = new \DateTime(); | ||
23 | } | ||
24 | } | ||
diff --git a/src/Wallabag/CoreBundle/Helper/EntriesExport.php b/src/Wallabag/CoreBundle/Helper/EntriesExport.php index 3d36a4c8..830798b8 100644 --- a/src/Wallabag/CoreBundle/Helper/EntriesExport.php +++ b/src/Wallabag/CoreBundle/Helper/EntriesExport.php | |||
@@ -2,12 +2,14 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Helper; | 3 | namespace Wallabag\CoreBundle\Helper; |
4 | 4 | ||
5 | use JMS\Serializer; | 5 | use Html2Text\Html2Text; |
6 | use JMS\Serializer\SerializationContext; | 6 | use JMS\Serializer\SerializationContext; |
7 | use JMS\Serializer\SerializerBuilder; | 7 | use JMS\Serializer\SerializerBuilder; |
8 | use PHPePub\Core\EPub; | 8 | use PHPePub\Core\EPub; |
9 | use PHPePub\Core\Structure\OPF\DublinCore; | 9 | use PHPePub\Core\Structure\OPF\DublinCore; |
10 | use Symfony\Component\HttpFoundation\Response; | 10 | use Symfony\Component\HttpFoundation\Response; |
11 | use Symfony\Component\Translation\TranslatorInterface; | ||
12 | use Wallabag\CoreBundle\Entity\Entry; | ||
11 | 13 | ||
12 | /** | 14 | /** |
13 | * This class doesn't have unit test BUT it's fully covered by a functional test with ExportControllerTest. | 15 | * This class doesn't have unit test BUT it's fully covered by a functional test with ExportControllerTest. |
@@ -16,21 +18,20 @@ class EntriesExport | |||
16 | { | 18 | { |
17 | private $wallabagUrl; | 19 | private $wallabagUrl; |
18 | private $logoPath; | 20 | private $logoPath; |
21 | private $translator; | ||
19 | private $title = ''; | 22 | private $title = ''; |
20 | private $entries = []; | 23 | private $entries = []; |
21 | private $authors = ['wallabag']; | 24 | private $author = 'wallabag'; |
22 | private $language = ''; | 25 | private $language = ''; |
23 | private $footerTemplate = '<div style="text-align:center;"> | ||
24 | <p>Produced by wallabag with %EXPORT_METHOD%</p> | ||
25 | <p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p> | ||
26 | </div>'; | ||
27 | 26 | ||
28 | /** | 27 | /** |
29 | * @param string $wallabagUrl Wallabag instance url | 28 | * @param TranslatorInterface $translator Translator service |
30 | * @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE | 29 | * @param string $wallabagUrl Wallabag instance url |
30 | * @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE | ||
31 | */ | 31 | */ |
32 | public function __construct($wallabagUrl, $logoPath) | 32 | public function __construct(TranslatorInterface $translator, $wallabagUrl, $logoPath) |
33 | { | 33 | { |
34 | $this->translator = $translator; | ||
34 | $this->wallabagUrl = $wallabagUrl; | 35 | $this->wallabagUrl = $wallabagUrl; |
35 | $this->logoPath = $logoPath; | 36 | $this->logoPath = $logoPath; |
36 | } | 37 | } |
@@ -63,7 +64,7 @@ class EntriesExport | |||
63 | */ | 64 | */ |
64 | public function updateTitle($method) | 65 | public function updateTitle($method) |
65 | { | 66 | { |
66 | $this->title = $method.' articles'; | 67 | $this->title = $method . ' articles'; |
67 | 68 | ||
68 | if ('entry' === $method) { | 69 | if ('entry' === $method) { |
69 | $this->title = $this->entries[0]->getTitle(); | 70 | $this->title = $this->entries[0]->getTitle(); |
@@ -73,6 +74,33 @@ class EntriesExport | |||
73 | } | 74 | } |
74 | 75 | ||
75 | /** | 76 | /** |
77 | * Sets the author for one entry or category. | ||
78 | * | ||
79 | * The publishers are used, or the domain name if empty. | ||
80 | * | ||
81 | * @param string $method Method to get articles | ||
82 | * | ||
83 | * @return EntriesExport | ||
84 | */ | ||
85 | public function updateAuthor($method) | ||
86 | { | ||
87 | if ('entry' !== $method) { | ||
88 | $this->author = $method . ' authors'; | ||
89 | |||
90 | return $this; | ||
91 | } | ||
92 | |||
93 | $this->author = $this->entries[0]->getDomainName(); | ||
94 | |||
95 | $publishedBy = $this->entries[0]->getPublishedBy(); | ||
96 | if (!empty($publishedBy)) { | ||
97 | $this->author = implode(', ', $publishedBy); | ||
98 | } | ||
99 | |||
100 | return $this; | ||
101 | } | ||
102 | |||
103 | /** | ||
76 | * Sets the output format. | 104 | * Sets the output format. |
77 | * | 105 | * |
78 | * @param string $format | 106 | * @param string $format |
@@ -81,7 +109,7 @@ class EntriesExport | |||
81 | */ | 109 | */ |
82 | public function exportAs($format) | 110 | public function exportAs($format) |
83 | { | 111 | { |
84 | $functionName = 'produce'.ucfirst($format); | 112 | $functionName = 'produce' . ucfirst($format); |
85 | if (method_exists($this, $functionName)) { | 113 | if (method_exists($this, $functionName)) { |
86 | return $this->$functionName(); | 114 | return $this->$functionName(); |
87 | } | 115 | } |
@@ -106,12 +134,12 @@ class EntriesExport | |||
106 | */ | 134 | */ |
107 | $content_start = | 135 | $content_start = |
108 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | 136 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
109 | ."<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" | 137 | . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" |
110 | .'<head>' | 138 | . '<head>' |
111 | ."<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n" | 139 | . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n" |
112 | ."<title>wallabag articles book</title>\n" | 140 | . "<title>wallabag articles book</title>\n" |
113 | ."</head>\n" | 141 | . "</head>\n" |
114 | ."<body>\n"; | 142 | . "<body>\n"; |
115 | 143 | ||
116 | $bookEnd = "</body>\n</html>\n"; | 144 | $bookEnd = "</body>\n</html>\n"; |
117 | 145 | ||
@@ -128,9 +156,7 @@ class EntriesExport | |||
128 | $book->setLanguage($this->language); | 156 | $book->setLanguage($this->language); |
129 | $book->setDescription('Some articles saved on my wallabag'); | 157 | $book->setDescription('Some articles saved on my wallabag'); |
130 | 158 | ||
131 | foreach ($this->authors as $author) { | 159 | $book->setAuthor($this->author, $this->author); |
132 | $book->setAuthor($author, $author); | ||
133 | } | ||
134 | 160 | ||
135 | // I hope this is a non existant address :) | 161 | // I hope this is a non existant address :) |
136 | $book->setPublisher('wallabag', 'wallabag'); | 162 | $book->setPublisher('wallabag', 'wallabag'); |
@@ -164,11 +190,11 @@ class EntriesExport | |||
164 | // in filenames, we limit to A-z/0-9 | 190 | // in filenames, we limit to A-z/0-9 |
165 | $filename = preg_replace('/[^A-Za-z0-9\-]/', '', $entry->getTitle()); | 191 | $filename = preg_replace('/[^A-Za-z0-9\-]/', '', $entry->getTitle()); |
166 | 192 | ||
167 | $chapter = $content_start.$entry->getContent().$bookEnd; | 193 | $chapter = $content_start . $entry->getContent() . $bookEnd; |
168 | $book->addChapter($entry->getTitle(), htmlspecialchars($filename).'.html', $chapter, true, EPub::EXTERNAL_REF_ADD); | 194 | $book->addChapter($entry->getTitle(), htmlspecialchars($filename) . '.html', $chapter, true, EPub::EXTERNAL_REF_ADD); |
169 | } | 195 | } |
170 | 196 | ||
171 | $book->addChapter('Notices', 'Cover2.html', $content_start.$this->getExportInformation('PHPePub').$bookEnd); | 197 | $book->addChapter('Notices', 'Cover2.html', $content_start . $this->getExportInformation('PHPePub') . $bookEnd); |
172 | 198 | ||
173 | return Response::create( | 199 | return Response::create( |
174 | $book->getBook(), | 200 | $book->getBook(), |
@@ -176,7 +202,7 @@ class EntriesExport | |||
176 | [ | 202 | [ |
177 | 'Content-Description' => 'File Transfer', | 203 | 'Content-Description' => 'File Transfer', |
178 | 'Content-type' => 'application/epub+zip', | 204 | 'Content-type' => 'application/epub+zip', |
179 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.epub"', | 205 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.epub"', |
180 | 'Content-Transfer-Encoding' => 'binary', | 206 | 'Content-Transfer-Encoding' => 'binary', |
181 | ] | 207 | ] |
182 | ); | 208 | ); |
@@ -196,7 +222,7 @@ class EntriesExport | |||
196 | * Book metadata | 222 | * Book metadata |
197 | */ | 223 | */ |
198 | $content->set('title', $this->title); | 224 | $content->set('title', $this->title); |
199 | $content->set('author', implode($this->authors)); | 225 | $content->set('author', $this->author); |
200 | $content->set('subject', $this->title); | 226 | $content->set('subject', $this->title); |
201 | 227 | ||
202 | /* | 228 | /* |
@@ -228,7 +254,7 @@ class EntriesExport | |||
228 | 'Accept-Ranges' => 'bytes', | 254 | 'Accept-Ranges' => 'bytes', |
229 | 'Content-Description' => 'File Transfer', | 255 | 'Content-Description' => 'File Transfer', |
230 | 'Content-type' => 'application/x-mobipocket-ebook', | 256 | 'Content-type' => 'application/x-mobipocket-ebook', |
231 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.mobi"', | 257 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.mobi"', |
232 | 'Content-Transfer-Encoding' => 'binary', | 258 | 'Content-Transfer-Encoding' => 'binary', |
233 | ] | 259 | ] |
234 | ); | 260 | ); |
@@ -247,7 +273,7 @@ class EntriesExport | |||
247 | * Book metadata | 273 | * Book metadata |
248 | */ | 274 | */ |
249 | $pdf->SetCreator(PDF_CREATOR); | 275 | $pdf->SetCreator(PDF_CREATOR); |
250 | $pdf->SetAuthor('wallabag'); | 276 | $pdf->SetAuthor($this->author); |
251 | $pdf->SetTitle($this->title); | 277 | $pdf->SetTitle($this->title); |
252 | $pdf->SetSubject('Articles via wallabag'); | 278 | $pdf->SetSubject('Articles via wallabag'); |
253 | $pdf->SetKeywords('wallabag'); | 279 | $pdf->SetKeywords('wallabag'); |
@@ -256,7 +282,7 @@ class EntriesExport | |||
256 | * Front page | 282 | * Front page |
257 | */ | 283 | */ |
258 | $pdf->AddPage(); | 284 | $pdf->AddPage(); |
259 | $intro = '<h1>'.$this->title.'</h1>'.$this->getExportInformation('tcpdf'); | 285 | $intro = '<h1>' . $this->title . '</h1>' . $this->getExportInformation('tcpdf'); |
260 | 286 | ||
261 | $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true); | 287 | $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true); |
262 | 288 | ||
@@ -269,7 +295,7 @@ class EntriesExport | |||
269 | } | 295 | } |
270 | 296 | ||
271 | $pdf->AddPage(); | 297 | $pdf->AddPage(); |
272 | $html = '<h1>'.$entry->getTitle().'</h1>'; | 298 | $html = '<h1>' . $entry->getTitle() . '</h1>'; |
273 | $html .= $entry->getContent(); | 299 | $html .= $entry->getContent(); |
274 | 300 | ||
275 | $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); | 301 | $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); |
@@ -284,7 +310,7 @@ class EntriesExport | |||
284 | [ | 310 | [ |
285 | 'Content-Description' => 'File Transfer', | 311 | 'Content-Description' => 'File Transfer', |
286 | 'Content-type' => 'application/pdf', | 312 | 'Content-type' => 'application/pdf', |
287 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.pdf"', | 313 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.pdf"', |
288 | 'Content-Transfer-Encoding' => 'binary', | 314 | 'Content-Transfer-Encoding' => 'binary', |
289 | ] | 315 | ] |
290 | ); | 316 | ); |
@@ -330,7 +356,7 @@ class EntriesExport | |||
330 | 200, | 356 | 200, |
331 | [ | 357 | [ |
332 | 'Content-type' => 'application/csv', | 358 | 'Content-type' => 'application/csv', |
333 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.csv"', | 359 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.csv"', |
334 | 'Content-Transfer-Encoding' => 'UTF-8', | 360 | 'Content-Transfer-Encoding' => 'UTF-8', |
335 | ] | 361 | ] |
336 | ); | 362 | ); |
@@ -348,7 +374,7 @@ class EntriesExport | |||
348 | 200, | 374 | 200, |
349 | [ | 375 | [ |
350 | 'Content-type' => 'application/json', | 376 | 'Content-type' => 'application/json', |
351 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.json"', | 377 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.json"', |
352 | 'Content-Transfer-Encoding' => 'UTF-8', | 378 | 'Content-Transfer-Encoding' => 'UTF-8', |
353 | ] | 379 | ] |
354 | ); | 380 | ); |
@@ -366,7 +392,7 @@ class EntriesExport | |||
366 | 200, | 392 | 200, |
367 | [ | 393 | [ |
368 | 'Content-type' => 'application/xml', | 394 | 'Content-type' => 'application/xml', |
369 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.xml"', | 395 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.xml"', |
370 | 'Content-Transfer-Encoding' => 'UTF-8', | 396 | 'Content-Transfer-Encoding' => 'UTF-8', |
371 | ] | 397 | ] |
372 | ); | 398 | ); |
@@ -382,8 +408,9 @@ class EntriesExport | |||
382 | $content = ''; | 408 | $content = ''; |
383 | $bar = str_repeat('=', 100); | 409 | $bar = str_repeat('=', 100); |
384 | foreach ($this->entries as $entry) { | 410 | foreach ($this->entries as $entry) { |
385 | $content .= "\n\n".$bar."\n\n".$entry->getTitle()."\n\n".$bar."\n\n"; | 411 | $content .= "\n\n" . $bar . "\n\n" . $entry->getTitle() . "\n\n" . $bar . "\n\n"; |
386 | $content .= trim(preg_replace('/\s+/S', ' ', strip_tags($entry->getContent())))."\n\n"; | 412 | $html = new Html2Text($entry->getContent(), ['do_links' => 'none', 'width' => 100]); |
413 | $content .= $html->getText(); | ||
387 | } | 414 | } |
388 | 415 | ||
389 | return Response::create( | 416 | return Response::create( |
@@ -391,7 +418,7 @@ class EntriesExport | |||
391 | 200, | 418 | 200, |
392 | [ | 419 | [ |
393 | 'Content-type' => 'text/plain', | 420 | 'Content-type' => 'text/plain', |
394 | 'Content-Disposition' => 'attachment; filename="'.$this->title.'.txt"', | 421 | 'Content-Disposition' => 'attachment; filename="' . $this->title . '.txt"', |
395 | 'Content-Transfer-Encoding' => 'UTF-8', | 422 | 'Content-Transfer-Encoding' => 'UTF-8', |
396 | ] | 423 | ] |
397 | ); | 424 | ); |
@@ -402,7 +429,7 @@ class EntriesExport | |||
402 | * | 429 | * |
403 | * @param string $format | 430 | * @param string $format |
404 | * | 431 | * |
405 | * @return Serializer | 432 | * @return string |
406 | */ | 433 | */ |
407 | private function prepareSerializingContent($format) | 434 | private function prepareSerializingContent($format) |
408 | { | 435 | { |
@@ -424,10 +451,12 @@ class EntriesExport | |||
424 | */ | 451 | */ |
425 | private function getExportInformation($type) | 452 | private function getExportInformation($type) |
426 | { | 453 | { |
427 | $info = str_replace('%EXPORT_METHOD%', $type, $this->footerTemplate); | 454 | $info = $this->translator->trans('export.footer_template', [ |
455 | '%method%' => $type, | ||
456 | ]); | ||
428 | 457 | ||
429 | if ('tcpdf' === $type) { | 458 | if ('tcpdf' === $type) { |
430 | return str_replace('%IMAGE%', '<img src="'.$this->logoPath.'" />', $info); | 459 | return str_replace('%IMAGE%', '<img src="' . $this->logoPath . '" />', $info); |
431 | } | 460 | } |
432 | 461 | ||
433 | return str_replace('%IMAGE%', '', $info); | 462 | return str_replace('%IMAGE%', '', $info); |
diff --git a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php index 1ac8feb1..4602a684 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; |
@@ -43,7 +41,7 @@ class HttpClientFactory | |||
43 | */ | 41 | */ |
44 | public function buildHttpClient() | 42 | public function buildHttpClient() |
45 | { | 43 | { |
46 | $this->logger->log('debug', 'Restricted access config enabled?', array('enabled' => (int) $this->restrictedAccess)); | 44 | $this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]); |
47 | 45 | ||
48 | if (0 === (int) $this->restrictedAccess) { | 46 | if (0 === (int) $this->restrictedAccess) { |
49 | return; | 47 | return; |
@@ -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..49c1ea41 100644 --- a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php +++ b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php | |||
@@ -6,6 +6,7 @@ use Pagerfanta\Adapter\AdapterInterface; | |||
6 | use Pagerfanta\Pagerfanta; | 6 | use Pagerfanta\Pagerfanta; |
7 | use Symfony\Component\Routing\Router; | 7 | use Symfony\Component\Routing\Router; |
8 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | 8 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
9 | use Wallabag\UserBundle\Entity\User; | ||
9 | 10 | ||
10 | class PreparePagerForEntries | 11 | class PreparePagerForEntries |
11 | { | 12 | { |
@@ -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..63f65067 100644 --- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\CoreBundle\Helper; | 3 | namespace Wallabag\CoreBundle\Helper; |
4 | 4 | ||
5 | use Psr\Log\LoggerInterface; | ||
5 | use RulerZ\RulerZ; | 6 | use RulerZ\RulerZ; |
6 | use Wallabag\CoreBundle\Entity\Entry; | 7 | use Wallabag\CoreBundle\Entity\Entry; |
7 | use Wallabag\CoreBundle\Entity\Tag; | 8 | use Wallabag\CoreBundle\Entity\Tag; |
@@ -14,12 +15,14 @@ class RuleBasedTagger | |||
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..0bfe5c57 --- /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(mb_convert_case($label, MB_CASE_LOWER)); | ||
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/Matches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php index e6bb03b1..e1610161 100644 --- a/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/Matches.php | |||
@@ -16,7 +16,7 @@ class Matches | |||
16 | { | 16 | { |
17 | public function __invoke($subject, $pattern) | 17 | public function __invoke($subject, $pattern) |
18 | { | 18 | { |
19 | if ($pattern[0] === "'") { | 19 | if ("'" === $pattern[0]) { |
20 | $pattern = sprintf("'%%%s%%'", substr($pattern, 1, -1)); | 20 | $pattern = sprintf("'%%%s%%'", substr($pattern, 1, -1)); |
21 | } | 21 | } |
22 | 22 | ||
diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php new file mode 100644 index 00000000..8e50f8d6 --- /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/Matches.php b/src/Wallabag/CoreBundle/Operator/PHP/Matches.php index 987ed2a5..bc0c3f8f 100644 --- a/src/Wallabag/CoreBundle/Operator/PHP/Matches.php +++ b/src/Wallabag/CoreBundle/Operator/PHP/Matches.php | |||
@@ -16,6 +16,6 @@ class Matches | |||
16 | { | 16 | { |
17 | public function __invoke($subject, $pattern) | 17 | public function __invoke($subject, $pattern) |
18 | { | 18 | { |
19 | return stripos($subject, $pattern) !== false; | 19 | return false !== stripos($subject, $pattern); |
20 | } | 20 | } |
21 | } | 21 | } |
diff --git a/src/Wallabag/CoreBundle/Operator/PHP/NotMatches.php b/src/Wallabag/CoreBundle/Operator/PHP/NotMatches.php new file mode 100644 index 00000000..bd4d887a --- /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 false === stripos($subject, $pattern); | ||
20 | } | ||
21 | } | ||
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index 4071301d..b5e35eff 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php | |||
@@ -3,29 +3,15 @@ | |||
3 | namespace Wallabag\CoreBundle\Repository; | 3 | namespace Wallabag\CoreBundle\Repository; |
4 | 4 | ||
5 | use Doctrine\ORM\EntityRepository; | 5 | use Doctrine\ORM\EntityRepository; |
6 | use Doctrine\ORM\Query; | 6 | use Doctrine\ORM\QueryBuilder; |
7 | use Pagerfanta\Adapter\DoctrineORMAdapter; | 7 | use Pagerfanta\Adapter\DoctrineORMAdapter; |
8 | use Pagerfanta\Pagerfanta; | 8 | use Pagerfanta\Pagerfanta; |
9 | use Wallabag\CoreBundle\Entity\Entry; | ||
9 | use Wallabag\CoreBundle\Entity\Tag; | 10 | use Wallabag\CoreBundle\Entity\Tag; |
10 | 11 | ||
11 | class EntryRepository extends EntityRepository | 12 | class EntryRepository extends EntityRepository |
12 | { | 13 | { |
13 | /** | 14 | /** |
14 | * Return a query builder to used by other getBuilderFor* method. | ||
15 | * | ||
16 | * @param int $userId | ||
17 | * | ||
18 | * @return QueryBuilder | ||
19 | */ | ||
20 | private function getBuilderByUser($userId) | ||
21 | { | ||
22 | return $this->createQueryBuilder('e') | ||
23 | ->andWhere('e.user = :userId')->setParameter('userId', $userId) | ||
24 | ->orderBy('e.createdAt', 'desc') | ||
25 | ; | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Retrieves all entries for a user. | 15 | * Retrieves all entries for a user. |
30 | * | 16 | * |
31 | * @param int $userId | 17 | * @param int $userId |
@@ -79,7 +65,7 @@ class EntryRepository extends EntityRepository | |||
79 | public function getBuilderForStarredByUser($userId) | 65 | public function getBuilderForStarredByUser($userId) |
80 | { | 66 | { |
81 | return $this | 67 | return $this |
82 | ->getBuilderByUser($userId) | 68 | ->getBuilderByUser($userId, 'starredAt', 'desc') |
83 | ->andWhere('e.isStarred = true') | 69 | ->andWhere('e.isStarred = true') |
84 | ; | 70 | ; |
85 | } | 71 | } |
@@ -89,7 +75,7 @@ class EntryRepository extends EntityRepository | |||
89 | * | 75 | * |
90 | * @param int $userId | 76 | * @param int $userId |
91 | * @param string $term | 77 | * @param string $term |
92 | * @param strint $currentRoute | 78 | * @param string $currentRoute |
93 | * | 79 | * |
94 | * @return QueryBuilder | 80 | * @return QueryBuilder |
95 | */ | 81 | */ |
@@ -108,7 +94,7 @@ class EntryRepository extends EntityRepository | |||
108 | 94 | ||
109 | // We lower() all parts here because PostgreSQL 'LIKE' verb is case-sensitive | 95 | // We lower() all parts here because PostgreSQL 'LIKE' verb is case-sensitive |
110 | $qb | 96 | $qb |
111 | ->andWhere('lower(e.content) LIKE lower(:term) OR lower(e.title) LIKE lower(:term) OR lower(e.url) LIKE lower(:term)')->setParameter('term', '%'.$term.'%') | 97 | ->andWhere('lower(e.content) LIKE lower(:term) OR lower(e.title) LIKE lower(:term) OR lower(e.url) LIKE lower(:term)')->setParameter('term', '%' . $term . '%') |
112 | ->leftJoin('e.tags', 't') | 98 | ->leftJoin('e.tags', 't') |
113 | ->groupBy('e.id'); | 99 | ->groupBy('e.id'); |
114 | 100 | ||
@@ -135,34 +121,54 @@ class EntryRepository extends EntityRepository | |||
135 | * @param int $userId | 121 | * @param int $userId |
136 | * @param bool $isArchived | 122 | * @param bool $isArchived |
137 | * @param bool $isStarred | 123 | * @param bool $isStarred |
124 | * @param bool $isPublic | ||
138 | * @param string $sort | 125 | * @param string $sort |
139 | * @param string $order | 126 | * @param string $order |
140 | * @param int $since | 127 | * @param int $since |
141 | * @param string $tags | 128 | * @param string $tags |
142 | * | 129 | * |
143 | * @return array | 130 | * @return Pagerfanta |
144 | */ | 131 | */ |
145 | public function findEntries($userId, $isArchived = null, $isStarred = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') | 132 | public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') |
146 | { | 133 | { |
147 | $qb = $this->createQueryBuilder('e') | 134 | $qb = $this->createQueryBuilder('e') |
148 | ->leftJoin('e.tags', 't') | 135 | ->leftJoin('e.tags', 't') |
149 | ->where('e.user =:userId')->setParameter('userId', $userId); | 136 | ->where('e.user = :userId')->setParameter('userId', $userId); |
150 | 137 | ||
151 | if (null !== $isArchived) { | 138 | if (null !== $isArchived) { |
152 | $qb->andWhere('e.isArchived =:isArchived')->setParameter('isArchived', (bool) $isArchived); | 139 | $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived); |
153 | } | 140 | } |
154 | 141 | ||
155 | if (null !== $isStarred) { | 142 | if (null !== $isStarred) { |
156 | $qb->andWhere('e.isStarred =:isStarred')->setParameter('isStarred', (bool) $isStarred); | 143 | $qb->andWhere('e.isStarred = :isStarred')->setParameter('isStarred', (bool) $isStarred); |
144 | } | ||
145 | |||
146 | if (null !== $isPublic) { | ||
147 | $qb->andWhere('e.uid IS ' . (true === $isPublic ? 'NOT' : '') . ' NULL'); | ||
157 | } | 148 | } |
158 | 149 | ||
159 | if ($since > 0) { | 150 | if ($since > 0) { |
160 | $qb->andWhere('e.updatedAt > :since')->setParameter('since', new \DateTime(date('Y-m-d H:i:s', $since))); | 151 | $qb->andWhere('e.updatedAt > :since')->setParameter('since', new \DateTime(date('Y-m-d H:i:s', $since))); |
161 | } | 152 | } |
162 | 153 | ||
163 | if ('' !== $tags) { | 154 | if (is_string($tags) && '' !== $tags) { |
164 | foreach (explode(',', $tags) as $tag) { | 155 | foreach (explode(',', $tags) as $i => $tag) { |
165 | $qb->andWhere('t.label = :label')->setParameter('label', $tag); | 156 | $entryAlias = 'e' . $i; |
157 | $tagAlias = 't' . $i; | ||
158 | |||
159 | // Complexe queries to ensure multiple tags are associated to an entry | ||
160 | // https://stackoverflow.com/a/6638146/569101 | ||
161 | $qb->andWhere($qb->expr()->in( | ||
162 | 'e.id', | ||
163 | $this->createQueryBuilder($entryAlias) | ||
164 | ->select($entryAlias . '.id') | ||
165 | ->leftJoin($entryAlias . '.tags', $tagAlias) | ||
166 | ->where($tagAlias . '.label = :label' . $i) | ||
167 | ->getDQL() | ||
168 | )); | ||
169 | |||
170 | // bound parameter to the main query builder | ||
171 | $qb->setParameter('label' . $i, $tag); | ||
166 | } | 172 | } |
167 | } | 173 | } |
168 | 174 | ||
@@ -182,7 +188,7 @@ class EntryRepository extends EntityRepository | |||
182 | * | 188 | * |
183 | * @param int $userId | 189 | * @param int $userId |
184 | * | 190 | * |
185 | * @return Entry | 191 | * @return array |
186 | */ | 192 | */ |
187 | public function findOneWithTags($userId) | 193 | public function findOneWithTags($userId) |
188 | { | 194 | { |
@@ -190,7 +196,7 @@ class EntryRepository extends EntityRepository | |||
190 | ->innerJoin('e.tags', 't') | 196 | ->innerJoin('e.tags', 't') |
191 | ->innerJoin('e.user', 'u') | 197 | ->innerJoin('e.user', 'u') |
192 | ->addSelect('t', 'u') | 198 | ->addSelect('t', 'u') |
193 | ->where('e.user=:userId')->setParameter('userId', $userId) | 199 | ->where('e.user = :userId')->setParameter('userId', $userId) |
194 | ; | 200 | ; |
195 | 201 | ||
196 | return $qb->getQuery()->getResult(); | 202 | return $qb->getQuery()->getResult(); |
@@ -328,47 +334,98 @@ class EntryRepository extends EntityRepository | |||
328 | * | 334 | * |
329 | * @return int | 335 | * @return int |
330 | */ | 336 | */ |
331 | public function countAllEntriesByUsername($userId) | 337 | public function countAllEntriesByUser($userId) |
332 | { | 338 | { |
333 | $qb = $this->createQueryBuilder('e') | 339 | $qb = $this->createQueryBuilder('e') |
334 | ->select('count(e)') | 340 | ->select('count(e)') |
335 | ->where('e.user=:userId')->setParameter('userId', $userId) | 341 | ->where('e.user = :userId')->setParameter('userId', $userId) |
336 | ; | 342 | ; |
337 | 343 | ||
338 | return $qb->getQuery()->getSingleScalarResult(); | 344 | return (int) $qb->getQuery()->getSingleScalarResult(); |
339 | } | 345 | } |
340 | 346 | ||
341 | /** | 347 | /** |
342 | * Count all entries for a tag and a user. | 348 | * Remove all entries for a user id. |
349 | * Used when a user want to reset all informations. | ||
343 | * | 350 | * |
344 | * @param int $userId | 351 | * @param int $userId |
345 | * @param int $tagId | 352 | */ |
353 | public function removeAllByUserId($userId) | ||
354 | { | ||
355 | $this->getEntityManager() | ||
356 | ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId') | ||
357 | ->setParameter('userId', $userId) | ||
358 | ->execute(); | ||
359 | } | ||
360 | |||
361 | public function removeArchivedByUserId($userId) | ||
362 | { | ||
363 | $this->getEntityManager() | ||
364 | ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId AND e.isArchived = TRUE') | ||
365 | ->setParameter('userId', $userId) | ||
366 | ->execute(); | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * Get id and url from all entries | ||
371 | * Used for the clean-duplicates command. | ||
372 | */ | ||
373 | public function findAllEntriesIdAndUrlByUserId($userId) | ||
374 | { | ||
375 | $qb = $this->createQueryBuilder('e') | ||
376 | ->select('e.id, e.url') | ||
377 | ->where('e.user = :userid')->setParameter(':userid', $userId); | ||
378 | |||
379 | return $qb->getQuery()->getArrayResult(); | ||
380 | } | ||
381 | |||
382 | /** | ||
383 | * @param int $userId | ||
346 | * | 384 | * |
347 | * @return int | 385 | * @return array |
348 | */ | 386 | */ |
349 | public function countAllEntriesByUserIdAndTagId($userId, $tagId) | 387 | public function findAllEntriesIdByUserId($userId = null) |
350 | { | 388 | { |
351 | $qb = $this->createQueryBuilder('e') | 389 | $qb = $this->createQueryBuilder('e') |
352 | ->select('count(e.id)') | 390 | ->select('e.id'); |
353 | ->leftJoin('e.tags', 't') | ||
354 | ->where('e.user=:userId')->setParameter('userId', $userId) | ||
355 | ->andWhere('t.id=:tagId')->setParameter('tagId', $tagId) | ||
356 | ; | ||
357 | 391 | ||
358 | return $qb->getQuery()->getSingleScalarResult(); | 392 | if (null !== $userId) { |
393 | $qb->where('e.user = :userid')->setParameter(':userid', $userId); | ||
394 | } | ||
395 | |||
396 | return $qb->getQuery()->getArrayResult(); | ||
359 | } | 397 | } |
360 | 398 | ||
361 | /** | 399 | /** |
362 | * Remove all entries for a user id. | 400 | * Find all entries by url and owner. |
363 | * Used when a user want to reset all informations. | ||
364 | * | 401 | * |
365 | * @param int $userId | 402 | * @param $url |
403 | * @param $userId | ||
404 | * | ||
405 | * @return array | ||
366 | */ | 406 | */ |
367 | public function removeAllByUserId($userId) | 407 | public function findAllByUrlAndUserId($url, $userId) |
368 | { | 408 | { |
369 | $this->getEntityManager() | 409 | return $this->createQueryBuilder('e') |
370 | ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId') | 410 | ->where('e.url = :url')->setParameter('url', urldecode($url)) |
371 | ->setParameter('userId', $userId) | 411 | ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) |
372 | ->execute(); | 412 | ->getQuery() |
413 | ->getResult(); | ||
414 | } | ||
415 | |||
416 | /** | ||
417 | * Return a query builder to used by other getBuilderFor* method. | ||
418 | * | ||
419 | * @param int $userId | ||
420 | * @param string $sortBy | ||
421 | * @param string $direction | ||
422 | * | ||
423 | * @return QueryBuilder | ||
424 | */ | ||
425 | private function getBuilderByUser($userId, $sortBy = 'createdAt', $direction = 'desc') | ||
426 | { | ||
427 | return $this->createQueryBuilder('e') | ||
428 | ->andWhere('e.user = :userId')->setParameter('userId', $userId) | ||
429 | ->orderBy(sprintf('e.%s', $sortBy), $direction); | ||
373 | } | 430 | } |
374 | } | 431 | } |
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..5c45211f 100644 --- a/src/Wallabag/CoreBundle/Repository/TagRepository.php +++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php | |||
@@ -3,6 +3,7 @@ | |||
3 | namespace Wallabag\CoreBundle\Repository; | 3 | namespace Wallabag\CoreBundle\Repository; |
4 | 4 | ||
5 | use Doctrine\ORM\EntityRepository; | 5 | use Doctrine\ORM\EntityRepository; |
6 | use Wallabag\CoreBundle\Entity\Tag; | ||
6 | 7 | ||
7 | class TagRepository extends EntityRepository | 8 | class TagRepository extends EntityRepository |
8 | { | 9 | { |
@@ -62,6 +63,27 @@ class TagRepository extends EntityRepository | |||
62 | } | 63 | } |
63 | 64 | ||
64 | /** | 65 | /** |
66 | * Find all tags (flat) per user with nb entries. | ||
67 | * | ||
68 | * @param int $userId | ||
69 | * | ||
70 | * @return array | ||
71 | */ | ||
72 | public function findAllFlatTagsWithNbEntries($userId) | ||
73 | { | ||
74 | return $this->createQueryBuilder('t') | ||
75 | ->select('t.id, t.label, t.slug, count(e.id) as nbEntries') | ||
76 | ->distinct(true) | ||
77 | ->leftJoin('t.entries', 'e') | ||
78 | ->where('e.user = :userId') | ||
79 | ->groupBy('t.id') | ||
80 | ->orderBy('t.slug') | ||
81 | ->setParameter('userId', $userId) | ||
82 | ->getQuery() | ||
83 | ->getArrayResult(); | ||
84 | } | ||
85 | |||
86 | /** | ||
65 | * Used only in test case to get a tag for our entry. | 87 | * Used only in test case to get a tag for our entry. |
66 | * | 88 | * |
67 | * @return Tag | 89 | * @return Tag |
@@ -76,4 +98,24 @@ class TagRepository extends EntityRepository | |||
76 | ->getQuery() | 98 | ->getQuery() |
77 | ->getSingleResult(); | 99 | ->getSingleResult(); |
78 | } | 100 | } |
101 | |||
102 | public function findForArchivedArticlesByUser($userId) | ||
103 | { | ||
104 | $ids = $this->createQueryBuilder('t') | ||
105 | ->select('t.id') | ||
106 | ->leftJoin('t.entries', 'e') | ||
107 | ->where('e.user = :userId')->setParameter('userId', $userId) | ||
108 | ->andWhere('e.isArchived = true') | ||
109 | ->groupBy('t.id') | ||
110 | ->orderBy('t.slug') | ||
111 | ->getQuery() | ||
112 | ->getArrayResult(); | ||
113 | |||
114 | $tags = []; | ||
115 | foreach ($ids as $id) { | ||
116 | $tags[] = $this->find($id); | ||
117 | } | ||
118 | |||
119 | return $tags; | ||
120 | } | ||
79 | } | 121 | } |
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 51d6ab47..31b16739 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,19 @@ 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 | - "@translator" |
136 | - '%domain_name%' | ||
116 | - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png | 137 | - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png |
117 | 138 | ||
118 | wallabag.operator.array.matches: | 139 | wallabag.operator.array.matches: |
@@ -125,6 +146,16 @@ services: | |||
125 | tags: | 146 | tags: |
126 | - { name: rulerz.operator, target: doctrine, operator: matches, inline: true } | 147 | - { name: rulerz.operator, target: doctrine, operator: matches, inline: true } |
127 | 148 | ||
149 | wallabag.operator.array.notmatches: | ||
150 | class: Wallabag\CoreBundle\Operator\PHP\NotMatches | ||
151 | tags: | ||
152 | - { name: rulerz.operator, target: native, operator: notmatches } | ||
153 | |||
154 | wallabag.operator.doctrine.notmatches: | ||
155 | class: Wallabag\CoreBundle\Operator\Doctrine\NotMatches | ||
156 | tags: | ||
157 | - { name: rulerz.operator, target: doctrine, operator: notmatches, inline: true } | ||
158 | |||
128 | wallabag_core.helper.redirect: | 159 | wallabag_core.helper.redirect: |
129 | class: Wallabag\CoreBundle\Helper\Redirect | 160 | class: Wallabag\CoreBundle\Helper\Redirect |
130 | arguments: | 161 | arguments: |
@@ -174,9 +205,15 @@ services: | |||
174 | class: Wallabag\CoreBundle\Helper\DownloadImages | 205 | class: Wallabag\CoreBundle\Helper\DownloadImages |
175 | arguments: | 206 | arguments: |
176 | - "@wallabag_core.entry.download_images.client" | 207 | - "@wallabag_core.entry.download_images.client" |
177 | - "%kernel.root_dir%/../web/assets/images" | 208 | - "%kernel.project_dir%/web/assets/images" |
178 | - '@=service(''craue_config'').get(''wallabag_url'')' | 209 | - '%domain_name%' |
179 | - "@logger" | 210 | - "@logger" |
180 | 211 | ||
181 | wallabag_core.entry.download_images.client: | 212 | wallabag_core.entry.download_images.client: |
182 | class: GuzzleHttp\Client | 213 | class: GuzzleHttp\Client |
214 | |||
215 | wallabag_core.helper.crypto_proxy: | ||
216 | class: Wallabag\CoreBundle\Helper\CryptoProxy | ||
217 | arguments: | ||
218 | - "%wallabag_core.site_credentials.encryption_key_path%" | ||
219 | - "@logger" | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index a52b579a..d0a38f7e 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | # filtered_tags: 'Filtered by tags:' | 169 | # filtered_tags: 'Filtered by tags:' |
165 | # filtered_search: 'Filtered by search:' | 170 | # filtered_search: 'Filtered by search:' |
166 | # untagged: 'Untagged entries' | 171 | # untagged: 'Untagged entries' |
172 | # all: 'All entries' | ||
167 | list: | 173 | list: |
168 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' | 174 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' |
169 | reading_time: 'estimeret læsetid' | 175 | reading_time: 'estimeret læsetid' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Ulæst' | 191 | unread_label: 'Ulæst' |
186 | preview_picture_label: 'Har et vist billede' | 192 | preview_picture_label: 'Har et vist billede' |
187 | preview_picture_help: 'Forhåndsvis billede' | 193 | preview_picture_help: 'Forhåndsvis billede' |
194 | # is_public_label: 'Has a public link' | ||
195 | # is_public_help: 'Public link' | ||
188 | language_label: 'Sprog' | 196 | language_label: 'Sprog' |
189 | # http_status_label: 'HTTP status' | 197 | # http_status_label: 'HTTP status' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'original' | 231 | original_article: 'original' |
224 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 232 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Oprettelsesdato' | 233 | created_at: 'Oprettelsesdato' |
234 | # published_at: 'Publication date' | ||
235 | # published_by: 'Published by' | ||
226 | new: | 236 | new: |
227 | page_title: 'Gem ny artikel' | 237 | page_title: 'Gem ny artikel' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | # page_title: 'Edit an entry' | 244 | # page_title: 'Edit an entry' |
235 | # title_label: 'Title' | 245 | # title_label: 'Title' |
236 | url_label: 'Url' | 246 | url_label: 'Url' |
237 | # is_public_label: 'Public' | ||
238 | save_label: 'Gem' | 247 | save_label: 'Gem' |
239 | public: | 248 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href=%wallabag_instance%'>wallabag</a>" | 249 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | # delete: "Are you sure you want to remove that article?" | ||
252 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'Om' | 255 | page_title: 'Om' |
@@ -385,6 +397,9 @@ tag: | |||
385 | # add: 'Add' | 397 | # add: 'Add' |
386 | # placeholder: 'You can add several tags, separated by a comma.' | 398 | # placeholder: 'You can add several tags, separated by a comma.' |
387 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | # page_title: 'Import' | 404 | # page_title: 'Import' |
390 | # page_description: 'Welcome to wallabag importer. Please select your previous service that you want to migrate.' | 405 | # page_description: 'Welcome to wallabag importer. Please select your previous service that you want to migrate.' |
@@ -510,6 +525,28 @@ user: | |||
510 | # delete: Delete | 525 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 526 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 527 | # back_to_list: Back to list |
528 | search: | ||
529 | # placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | # page_title: Site credentials management | ||
533 | # new_site_credential: Create a credential | ||
534 | # edit_site_credential: Edit an existing credential | ||
535 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | # list: | ||
537 | # actions: Actions | ||
538 | # edit_action: Edit | ||
539 | # yes: Yes | ||
540 | # no: No | ||
541 | # create_new_one: Create a new credential | ||
542 | # form: | ||
543 | # username_label: 'Username' | ||
544 | # host_label: 'Host' | ||
545 | # password_label: 'Password' | ||
546 | # save: Save | ||
547 | # delete: Delete | ||
548 | # delete_confirm: Are you sure? | ||
549 | # back_to_list: Back to list | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | # page_title: An error occurred | 552 | # page_title: An error occurred |
@@ -528,6 +565,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 565 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 566 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 567 | # entries_reset: Entries reset |
568 | # archived_reset: Archived entries deleted | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | # entry_already_saved: 'Entry already saved on %date%' | 571 | # entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 600 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 601 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 602 | # deleted: 'User "%username%" deleted' |
603 | site_credential: | ||
604 | notice: | ||
605 | # added: 'Site credential for "%host%" added' | ||
606 | # updated: 'Site credential for "%host%" updated' | ||
607 | # 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..158762a9 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: 'Zugangsdaten' | ||
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' |
@@ -46,7 +47,7 @@ footer: | |||
46 | social: 'Soziales' | 47 | social: 'Soziales' |
47 | powered_by: 'angetrieben von' | 48 | powered_by: 'angetrieben von' |
48 | about: 'Über' | 49 | about: 'Über' |
49 | stats: Seit %user_creation% hast du %nb_archives% Artikel gelesen. Das sind ungefähr %per_day% pro Tag! | 50 | stats: 'Seit %user_creation% hast du %nb_archives% Artikel gelesen. Das sind ungefähr %per_day% pro Tag!' |
50 | 51 | ||
51 | config: | 52 | config: |
52 | page_title: 'Einstellungen' | 53 | page_title: 'Einstellungen' |
@@ -74,8 +75,9 @@ config: | |||
74 | label: 'Wohin soll nach dem Gelesenmarkieren eines Artikels weitergeleitet werden?' | 75 | label: 'Wohin soll nach dem Gelesenmarkieren eines Artikels weitergeleitet werden?' |
75 | redirect_homepage: 'Zur Homepage' | 76 | redirect_homepage: 'Zur Homepage' |
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-App' |
80 | android_instruction: "Klicke hier, um deine Android-App auszufüllen" | ||
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: 'Alle' | ||
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" |
@@ -100,17 +103,18 @@ config: | |||
100 | twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' | 103 | twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' |
101 | help_twoFactorAuthentication: "Wenn du 2FA aktivierst, wirst du bei jedem Login einen Code per E-Mail bekommen." | 104 | help_twoFactorAuthentication: "Wenn du 2FA aktivierst, wirst du bei jedem Login einen Code per E-Mail bekommen." |
102 | delete: | 105 | delete: |
103 | title: Lösche mein Konto (a.k.a Gefahrenzone) | 106 | title: 'Lösche mein Konto (a.k.a Gefahrenzone)' |
104 | description: Wenn du dein Konto löschst, werden ALL deine Artikel, ALL deine Tags, ALL deine Anmerkungen und dein Konto dauerhaft gelöscht (kann NICHT RÜCKGÄNGIG gemacht werden). Du wirst anschließend ausgeloggt. | 107 | description: 'Wenn du dein Konto löschst, werden ALL deine Artikel, ALL deine Tags, ALL deine Anmerkungen und dein Konto dauerhaft gelöscht (kann NICHT RÜCKGÄNGIG gemacht werden). Du wirst anschließend ausgeloggt.' |
105 | confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN) | 108 | confirm: 'Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN)' |
106 | button: Lösche mein Konto | 109 | button: 'Lösche mein Konto' |
107 | reset: | 110 | reset: |
108 | title: Zurücksetzen (a.k.a Gefahrenzone) | 111 | title: 'Zurücksetzen (a.k.a Gefahrenzone)' |
109 | description: Beim Nutzen der folgenden Schaltflächenhast du die Möglichkeit, einige Informationen von deinem Konto zu entfernen. Sei dir bewusst, dass dies NICHT RÜCKGÄNGIG zu machen ist. | 112 | description: 'Beim Nutzen der folgenden Schaltflächenhast du die Möglichkeit, einige Informationen von deinem Konto zu entfernen. Sei dir bewusst, dass dies NICHT RÜCKGÄNGIG zu machen ist.' |
110 | annotations: Entferne ALLE Annotationen | 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' |
113 | confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN) | 116 | archived: 'Entferne ALLE archivierten Einträge' |
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." |
116 | old_password_label: 'Altes Kennwort' | 120 | old_password_label: 'Altes Kennwort' |
@@ -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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | filtered_tags: 'Gefiltert nach Tags:' | 169 | filtered_tags: 'Gefiltert nach Tags:' |
165 | filtered_search: 'Gefiltert nach Suche:' | 170 | filtered_search: 'Gefiltert nach Suche:' |
166 | untagged: 'Nicht getaggte Einträge' | 171 | untagged: 'Nicht getaggte Einträge' |
172 | all: 'Alle Einträge' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.' | 174 | number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.' |
169 | reading_time: 'geschätzte Lesezeit' | 175 | reading_time: 'geschätzte Lesezeit' |
@@ -172,7 +178,7 @@ entry: | |||
172 | number_of_tags: '{1}und ein anderer Tag|]1,Inf[und %count% andere Tags' | 178 | number_of_tags: '{1}und ein anderer Tag|]1,Inf[und %count% andere Tags' |
173 | reading_time_minutes_short: '%readingTime% min' | 179 | reading_time_minutes_short: '%readingTime% min' |
174 | reading_time_less_one_minute_short: '< 1 min' | 180 | reading_time_less_one_minute_short: '< 1 min' |
175 | original_article: 'Original' | 181 | original_article: 'Originalartikel' |
176 | toogle_as_read: 'Gelesen-Status ändern' | 182 | toogle_as_read: 'Gelesen-Status ändern' |
177 | toogle_as_star: 'Favoriten-Status ändern' | 183 | toogle_as_star: 'Favoriten-Status ändern' |
178 | delete: 'Löschen' | 184 | delete: 'Löschen' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Ungelesene' | 191 | unread_label: 'Ungelesene' |
186 | preview_picture_label: 'Vorschaubild vorhanden' | 192 | preview_picture_label: 'Vorschaubild vorhanden' |
187 | preview_picture_help: 'Vorschaubild' | 193 | preview_picture_help: 'Vorschaubild' |
194 | is_public_label: 'Hat einen öffentlichen Link' | ||
195 | is_public_help: 'Öffentlicher Link' | ||
188 | language_label: 'Sprache' | 196 | language_label: 'Sprache' |
189 | http_status_label: 'HTTP-Status' | 197 | http_status_label: 'HTTP-Status' |
190 | reading_time: | 198 | reading_time: |
@@ -220,9 +228,11 @@ entry: | |||
220 | label: 'Probleme?' | 228 | label: 'Probleme?' |
221 | description: 'Erscheint dieser Artikel falsch?' | 229 | description: 'Erscheint dieser Artikel falsch?' |
222 | edit_title: 'Titel ändern' | 230 | edit_title: 'Titel ändern' |
223 | original_article: 'original' | 231 | original_article: 'Originalartikel' |
224 | annotations_on_the_entry: '{0} Keine Anmerkungen|{1} Eine Anmerkung|]1,Inf[ %count% Anmerkungen' | 232 | annotations_on_the_entry: '{0} Keine Anmerkungen|{1} Eine Anmerkung|]1,Inf[ %count% Anmerkungen' |
225 | created_at: 'Erstellungsdatum' | 233 | created_at: 'Erstellungsdatum' |
234 | published_at: 'Erscheinungsdatum' | ||
235 | published_by: 'Veröffentlicht von' | ||
226 | new: | 236 | new: |
227 | page_title: 'Neuen Artikel speichern' | 237 | page_title: 'Neuen Artikel speichern' |
228 | placeholder: 'https://website.de' | 238 | placeholder: 'https://website.de' |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: 'Eintrag bearbeiten' | 244 | page_title: 'Eintrag bearbeiten' |
235 | title_label: 'Titel' | 245 | title_label: 'Titel' |
236 | url_label: 'URL' | 246 | url_label: 'URL' |
237 | is_public_label: 'Öffentlich' | ||
238 | save_label: 'Speichern' | 247 | save_label: 'Speichern' |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "Dieser Artikel wurde mittels <a href='%wallabag_instance%'>wallabag</a> geteilt" | 249 | shared_by_wallabag: 'Dieser Artikel wurde von %username% mittels <a href="%wallabag_instance%">wallabag</a> geteilt' |
250 | confirm: | ||
251 | delete: 'Bist du sicher, dass du diesen Artikel löschen möchtest?' | ||
252 | delete_tag: 'Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?' | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'Über' | 255 | page_title: 'Über' |
@@ -295,32 +307,32 @@ howto: | |||
295 | bookmarklet: | 307 | bookmarklet: |
296 | description: 'Ziehe diesen Link in deine Lesezeichenleiste:' | 308 | description: 'Ziehe diesen Link in deine Lesezeichenleiste:' |
297 | shortcuts: | 309 | shortcuts: |
298 | page_description: Dies sind die verfügbaren Tastenkürzel in wallabag. | 310 | page_description: 'Dies sind die verfügbaren Tastenkürzel in wallabag.' |
299 | shortcut: Tastenkürzel | 311 | shortcut: 'Tastenkürzel' |
300 | action: Aktion | 312 | action: 'Aktion' |
301 | all_pages_title: Tastenkürzel auf allen Seiten | 313 | all_pages_title: 'Tastenkürzel auf allen Seiten' |
302 | go_unread: Zu ungelesen gehen | 314 | go_unread: 'Zu ungelesen gehen' |
303 | go_starred: Zu Favoriten gehen | 315 | go_starred: 'Zu Favoriten gehen' |
304 | go_archive: Zu archivierten gehen | 316 | go_archive: 'Zu archivierten gehen' |
305 | go_all: Zu allen Artikel gehen | 317 | go_all: 'Zu allen Artikel gehen' |
306 | go_tags: Zu den Tags gehen | 318 | go_tags: 'Zu den Tags gehen' |
307 | go_config: Einstellungen öffnen | 319 | go_config: 'Einstellungen öffnen' |
308 | go_import: Import-Sektion öffnen | 320 | go_import: 'Import-Sektion öffnen' |
309 | go_developers: Zur Entwickler-Seite gehen | 321 | go_developers: 'Zur Entwickler-Seite gehen' |
310 | go_howto: Zum Howto gehen (diese Seite!) | 322 | go_howto: 'Zum Howto gehen (diese Seite!)' |
311 | go_logout: Abmelden | 323 | go_logout: 'Abmelden' |
312 | list_title: Tastenkürzel verfügbar auf den Listen-Seiten | 324 | list_title: 'Tastenkürzel verfügbar auf den Listen-Seiten' |
313 | search: Suche | 325 | search: 'Suche' |
314 | article_title: Tastenkürzel verfügbar auf der Artikel-Seite | 326 | article_title: 'Tastenkürzel verfügbar auf der Artikel-Seite' |
315 | open_original: Original-Artikel öffnen | 327 | open_original: 'Original-Artikel öffnen' |
316 | toggle_favorite: Favorit-Status für den Artikel ändern | 328 | toggle_favorite: 'Favorit-Status für den Artikel ändern' |
317 | toggle_archive: Archiviert-Status für den Artikel ändern | 329 | toggle_archive: 'Archiviert-Status für den Artikel ändern' |
318 | delete: Artikel löschen | 330 | delete: 'Artikel löschen' |
319 | material_title: Tastenkürzel des Material-Theme | 331 | material_title: 'Tastenkürzel des Material-Theme' |
320 | add_link: Neuen Link hinzufügen | 332 | add_link: 'Neuen Link hinzufügen' |
321 | hide_form: Aktuelles Formular verstecken (Suche oder neuer Link) | 333 | hide_form: 'Aktuelles Formular verstecken (Suche oder neuer Link)' |
322 | arrows_navigation: Durch Artikel navigieren | 334 | arrows_navigation: 'Durch Artikel navigieren' |
323 | open_article: Gewählten Artikel anzeigen | 335 | open_article: 'Gewählten Artikel anzeigen' |
324 | 336 | ||
325 | quickstart: | 337 | quickstart: |
326 | page_title: 'Schnelleinstieg' | 338 | page_title: 'Schnelleinstieg' |
@@ -385,6 +397,9 @@ tag: | |||
385 | add: 'Hinzufügen' | 397 | add: 'Hinzufügen' |
386 | placeholder: 'Du kannst verschiedene Tags, getrennt von einem Komma, hinzufügen.' | 398 | placeholder: 'Du kannst verschiedene Tags, getrennt von einem Komma, hinzufügen.' |
387 | 399 | ||
400 | export: | ||
401 | footer_template: '<div style="text-align:center;"><p>Generiert von wallabag mit Hilfe von %method%</p><p>Bitte öffne <a href="https://github.com/wallabag/wallabag/issues">ein Ticket</a> wenn du ein Problem mit der Darstellung von diesem E-Book auf deinem Gerät hast.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'Importieren' | 404 | page_title: 'Importieren' |
390 | page_description: 'Willkommen beim wallabag-Importer. Wähle deinen vorherigen Service aus, von dem du die Daten migrieren willst.' | 405 | page_description: 'Willkommen beim wallabag-Importer. Wähle deinen vorherigen Service aus, von dem du die Daten migrieren willst.' |
@@ -471,7 +486,7 @@ developer: | |||
471 | field_id: 'Client-ID' | 486 | field_id: 'Client-ID' |
472 | field_secret: 'Client-Secret' | 487 | field_secret: 'Client-Secret' |
473 | back: 'Zurück' | 488 | back: 'Zurück' |
474 | read_howto: 'Lese des How-To zu "Wie erstelle ich meine erste Anwendung"' | 489 | read_howto: 'Lese das How-To zu "Wie erstelle ich meine erste Anwendung"' |
475 | howto: | 490 | howto: |
476 | page_title: 'API-Client-Verwaltung > Wie erstelle ich meine erste Anwendung' | 491 | page_title: 'API-Client-Verwaltung > Wie erstelle ich meine erste Anwendung' |
477 | description: | 492 | description: |
@@ -486,16 +501,16 @@ developer: | |||
486 | back: 'Zurück' | 501 | back: 'Zurück' |
487 | 502 | ||
488 | user: | 503 | user: |
489 | page_title: Benutzerverwaltung | 504 | page_title: 'Benutzerverwaltung' |
490 | new_user: Erstelle einen neuen Benutzer | 505 | new_user: 'Erstelle einen neuen Benutzer' |
491 | edit_user: Bearbeite einen existierenden Benutzer | 506 | edit_user: 'Bearbeite einen existierenden Benutzer' |
492 | description: "Hier kannst du alle Benutzer verwalten (erstellen, bearbeiten und löschen)" | 507 | description: "Hier kannst du alle Benutzer verwalten (erstellen, bearbeiten und löschen)" |
493 | list: | 508 | list: |
494 | actions: Aktionen | 509 | actions: 'Aktionen' |
495 | edit_action: Bearbeiten | 510 | edit_action: 'Bearbeiten' |
496 | yes: Ja | 511 | yes: 'Ja' |
497 | no: Nein | 512 | no: 'Nein' |
498 | create_new_one: Erstelle einen neuen Benutzer | 513 | create_new_one: 'Erstelle einen neuen Benutzer' |
499 | form: | 514 | form: |
500 | username_label: 'Benutzername' | 515 | username_label: 'Benutzername' |
501 | name_label: 'Name' | 516 | name_label: 'Name' |
@@ -505,29 +520,52 @@ user: | |||
505 | email_label: 'E-Mail-Adresse' | 520 | email_label: 'E-Mail-Adresse' |
506 | enabled_label: 'Aktiviert' | 521 | enabled_label: 'Aktiviert' |
507 | last_login_label: 'Letzter Login' | 522 | last_login_label: 'Letzter Login' |
508 | twofactor_label: Zwei-Faktor-Authentifizierung | 523 | twofactor_label: 'Zwei-Faktor-Authentifizierung' |
509 | save: Speichern | 524 | save: 'Speichern' |
510 | delete: Löschen | 525 | delete: 'Löschen' |
511 | delete_confirm: Bist du sicher? | 526 | delete_confirm: 'Bist du sicher?' |
512 | back_to_list: Zurück zur Liste | 527 | back_to_list: 'Zurück zur Liste' |
528 | search: | ||
529 | placeholder: 'Filtere nach Benutzer oder E-Mail-Adresse' | ||
530 | |||
531 | site_credential: | ||
532 | page_title: 'Verwaltung Zugangsdaten' | ||
533 | new_site_credential: 'Zugangsdaten anlegen' | ||
534 | edit_site_credential: 'Bestehende Zugangsdaten bearbeiten' | ||
535 | description: 'Hier kannst du alle Zugangsdaten für Webseiten verwalten, die diese benötigen (anlegen, bearbeiten und löschen), wie z. B. eine Paywall, eine Authentifizierung, etc.' | ||
536 | list: | ||
537 | actions: 'Aktionen' | ||
538 | edit_action: 'Bearbeiten' | ||
539 | yes: 'Ja' | ||
540 | no: 'Nein' | ||
541 | create_new_one: 'Einen neuen Seitenzugang anlegen' | ||
542 | form: | ||
543 | username_label: 'Benutzername' | ||
544 | host_label: 'Host' | ||
545 | password_label: 'Passwort' | ||
546 | save: 'Speichern' | ||
547 | delete: 'Löschen' | ||
548 | delete_confirm: 'Bist du sicher?' | ||
549 | back_to_list: 'Zurück zur Liste' | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | page_title: Ein Fehler ist aufgetreten | 552 | page_title: 'Ein Fehler ist aufgetreten' |
516 | 553 | ||
517 | flashes: | 554 | flashes: |
518 | config: | 555 | config: |
519 | notice: | 556 | notice: |
520 | config_saved: 'Konfiguration gespeichert.' | 557 | config_saved: 'Konfiguration gespeichert.' |
521 | password_updated: 'Kennwort aktualisiert' | 558 | password_updated: 'Kennwort aktualisiert' |
522 | password_not_updated_demo: "Im Testmodus kannst du das Kennwort nicht ändern." | 559 | password_not_updated_demo: 'Im Testmodus kannst du das Kennwort nicht ändern.' |
523 | user_updated: 'Information aktualisiert' | 560 | user_updated: 'Information aktualisiert' |
524 | rss_updated: 'RSS-Informationen aktualisiert' | 561 | rss_updated: 'RSS-Informationen aktualisiert' |
525 | tagging_rules_updated: 'Tagging-Regeln aktualisiert' | 562 | tagging_rules_updated: 'Tagging-Regeln aktualisiert' |
526 | tagging_rules_deleted: 'Tagging-Regel gelöscht' | 563 | tagging_rules_deleted: 'Tagging-Regel gelöscht' |
527 | rss_token_updated: 'RSS-Token aktualisiert' | 564 | rss_token_updated: 'RSS-Token aktualisiert' |
528 | annotations_reset: Anmerkungen zurücksetzen | 565 | annotations_reset: 'Anmerkungen zurücksetzen' |
529 | tags_reset: Tags zurücksetzen | 566 | tags_reset: 'Tags zurücksetzen' |
530 | entries_reset: Einträge zurücksetzen | 567 | entries_reset: 'Einträge zurücksetzen' |
568 | archived_reset: 'Archiverte Einträge zurücksetzen' | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'Eintrag bereits am %date% gespeichert' | 571 | entry_already_saved: 'Eintrag bereits am %date% gespeichert' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'Benutzer "%username%" hinzugefügt' | 600 | added: 'Benutzer "%username%" hinzugefügt' |
563 | updated: 'Benutzer "%username%" aktualisiert' | 601 | updated: 'Benutzer "%username%" aktualisiert' |
564 | deleted: 'Benutzer "%username%" gelöscht' | 602 | deleted: 'Benutzer "%username%" gelöscht' |
603 | site_credential: | ||
604 | notice: | ||
605 | added: 'Zugangsdaten für "%host%" hinzugefügt' | ||
606 | updated: 'Zugangsdaten für "%host%" aktualisiert' | ||
607 | deleted: 'Zugangsdaten für "%host%" gelöscht' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index c72e5958..de3e11fe 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | filtered_tags: 'Filtered by tags:' | 169 | filtered_tags: 'Filtered by tags:' |
165 | filtered_search: 'Filtered by search:' | 170 | filtered_search: 'Filtered by search:' |
166 | untagged: 'Untagged entries' | 171 | untagged: 'Untagged entries' |
172 | all: 'All entries' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.' | 174 | number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.' |
169 | reading_time: 'estimated reading time' | 175 | reading_time: 'estimated reading time' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Unread' | 191 | unread_label: 'Unread' |
186 | preview_picture_label: 'Has a preview picture' | 192 | preview_picture_label: 'Has a preview picture' |
187 | preview_picture_help: 'Preview picture' | 193 | preview_picture_help: 'Preview picture' |
194 | is_public_label: 'Has a public link' | ||
195 | is_public_help: 'Public link' | ||
188 | language_label: 'Language' | 196 | language_label: 'Language' |
189 | http_status_label: 'HTTP status' | 197 | http_status_label: 'HTTP status' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'original' | 231 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 232 | annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Creation date' | 233 | created_at: 'Creation date' |
234 | published_at: 'Publication date' | ||
235 | published_by: 'Published by' | ||
226 | new: | 236 | new: |
227 | page_title: 'Save new entry' | 237 | page_title: 'Save new entry' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: 'Edit an entry' | 244 | page_title: 'Edit an entry' |
235 | title_label: 'Title' | 245 | title_label: 'Title' |
236 | url_label: 'Url' | 246 | url_label: 'Url' |
237 | is_public_label: 'Public' | ||
238 | save_label: 'Save' | 247 | save_label: 'Save' |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 249 | shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | delete: "Are you sure you want to remove that article?" | ||
252 | delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'About' | 255 | page_title: 'About' |
@@ -385,6 +397,9 @@ tag: | |||
385 | add: 'Add' | 397 | add: 'Add' |
386 | placeholder: 'You can add several tags, separated by a comma.' | 398 | placeholder: 'You can add several tags, separated by a comma.' |
387 | 399 | ||
400 | export: | ||
401 | footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'Import' | 404 | page_title: 'Import' |
390 | page_description: 'Welcome to wallabag importer. Please select your previous service from which you want to migrate.' | 405 | page_description: 'Welcome to wallabag importer. Please select your previous service from which you want to migrate.' |
@@ -510,6 +525,28 @@ user: | |||
510 | delete: Delete | 525 | delete: Delete |
511 | delete_confirm: Are you sure? | 526 | delete_confirm: Are you sure? |
512 | back_to_list: Back to list | 527 | back_to_list: Back to list |
528 | search: | ||
529 | placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | page_title: Site credentials management | ||
533 | new_site_credential: Create a credential | ||
534 | edit_site_credential: Edit an existing credential | ||
535 | description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | list: | ||
537 | actions: Actions | ||
538 | edit_action: Edit | ||
539 | yes: Yes | ||
540 | no: No | ||
541 | create_new_one: Create a new credential | ||
542 | form: | ||
543 | username_label: 'Username' | ||
544 | host_label: 'Host' | ||
545 | password_label: 'Password' | ||
546 | save: Save | ||
547 | delete: Delete | ||
548 | delete_confirm: Are you sure? | ||
549 | back_to_list: Back to list | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | page_title: An error occurred | 552 | page_title: An error occurred |
@@ -528,6 +565,7 @@ flashes: | |||
528 | annotations_reset: Annotations reset | 565 | annotations_reset: Annotations reset |
529 | tags_reset: Tags reset | 566 | tags_reset: Tags reset |
530 | entries_reset: Entries reset | 567 | entries_reset: Entries reset |
568 | archived_reset: Archived entries deleted | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'Entry already saved on %date%' | 571 | entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'User "%username%" added' | 600 | added: 'User "%username%" added' |
563 | updated: 'User "%username%" updated' | 601 | updated: 'User "%username%" updated' |
564 | deleted: 'User "%username%" deleted' | 602 | deleted: 'User "%username%" deleted' |
603 | site_credential: | ||
604 | notice: | ||
605 | added: 'Site credential for "%host%" added' | ||
606 | updated: 'Site credential for "%host%" updated' | ||
607 | 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..6dfc1525 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | filtered_tags: 'Filtrado por etiquetas:' | 169 | filtered_tags: 'Filtrado por etiquetas:' |
165 | filtered_search: 'Filtrado por búsqueda:' | 170 | filtered_search: 'Filtrado por búsqueda:' |
166 | untagged: 'Artículos sin etiquetas' | 171 | untagged: 'Artículos sin etiquetas' |
172 | all: "Todos los artículos" | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.' | 174 | number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.' |
169 | reading_time: 'tiempo estimado de lectura' | 175 | reading_time: 'tiempo estimado de lectura' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Sin leer' | 191 | unread_label: 'Sin leer' |
186 | preview_picture_label: 'Tiene imagen de previsualización' | 192 | preview_picture_label: 'Tiene imagen de previsualización' |
187 | preview_picture_help: 'Imagen de previsualización' | 193 | preview_picture_help: 'Imagen de previsualización' |
194 | # is_public_label: 'Has a public link' | ||
195 | # is_public_help: 'Public link' | ||
188 | language_label: 'Idioma' | 196 | language_label: 'Idioma' |
189 | http_status_label: 'Código de estado HTTP' | 197 | http_status_label: 'Código de estado HTTP' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'original' | 231 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} Sin anotaciones|{1} Una anotación|]1,Inf[ %count% anotaciones' | 232 | annotations_on_the_entry: '{0} Sin anotaciones|{1} Una anotación|]1,Inf[ %count% anotaciones' |
225 | created_at: 'Fecha de creación' | 233 | created_at: 'Fecha de creación' |
234 | # published_at: 'Publication date' | ||
235 | # published_by: 'Published by' | ||
226 | new: | 236 | new: |
227 | page_title: 'Guardar un nuevo artículo' | 237 | page_title: 'Guardar un nuevo artículo' |
228 | placeholder: 'http://sitioweb.com' | 238 | placeholder: 'http://sitioweb.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: 'Editar un artículo' | 244 | page_title: 'Editar un artículo' |
235 | title_label: 'Título' | 245 | title_label: 'Título' |
236 | url_label: 'URL' | 246 | url_label: 'URL' |
237 | is_public_label: 'Es público' | ||
238 | save_label: 'Guardar' | 247 | save_label: 'Guardar' |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "Este artículo se ha compartido con <a href='%wallabag_instance%'>wallabag</a>" | 249 | shared_by_wallabag: "Este artículo se ha compartido con <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | # delete: "Are you sure you want to remove that article?" | ||
252 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'Acerca de' | 255 | page_title: 'Acerca de' |
@@ -385,6 +397,9 @@ tag: | |||
385 | add: 'Añadir' | 397 | add: 'Añadir' |
386 | placeholder: 'Puedes añadir varias etiquetas, separadas por una coma.' | 398 | placeholder: 'Puedes añadir varias etiquetas, separadas por una coma.' |
387 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'Importar' | 404 | page_title: 'Importar' |
390 | page_description: 'Bienvenido a la herramienta de importación de wallabag. Seleccione el servicio desde el que desea migrar.' | 405 | page_description: 'Bienvenido a la herramienta de importación de wallabag. Seleccione el servicio desde el que desea migrar.' |
@@ -510,6 +525,28 @@ user: | |||
510 | delete: Eliminar | 525 | delete: Eliminar |
511 | delete_confirm: ¿Estás seguro? | 526 | delete_confirm: ¿Estás seguro? |
512 | back_to_list: Volver a la lista | 527 | back_to_list: Volver a la lista |
528 | search: | ||
529 | # placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | # page_title: Site credentials management | ||
533 | # new_site_credential: Create a credential | ||
534 | # edit_site_credential: Edit an existing credential | ||
535 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | # list: | ||
537 | # actions: Actions | ||
538 | # edit_action: Edit | ||
539 | # yes: Yes | ||
540 | # no: No | ||
541 | # create_new_one: Create a new credential | ||
542 | # form: | ||
543 | # username_label: 'Username' | ||
544 | # host_label: 'Host' | ||
545 | # password_label: 'Password' | ||
546 | # save: Save | ||
547 | # delete: Delete | ||
548 | # delete_confirm: Are you sure? | ||
549 | # back_to_list: Back to list | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | page_title: Ha ocurrido un error | 552 | page_title: Ha ocurrido un error |
@@ -528,6 +565,7 @@ flashes: | |||
528 | annotations_reset: Anotaciones reiniciadas | 565 | annotations_reset: Anotaciones reiniciadas |
529 | tags_reset: Etiquetas reiniciadas | 566 | tags_reset: Etiquetas reiniciadas |
530 | entries_reset: Artículos reiniciados | 567 | entries_reset: Artículos reiniciados |
568 | # archived_reset: Archived entries deleted | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'Artículo ya guardado el %fecha%' | 571 | entry_already_saved: 'Artículo ya guardado el %fecha%' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'Añadido el usuario "%username%"' | 600 | added: 'Añadido el usuario "%username%"' |
563 | updated: 'Actualizado el usuario "%username%"' | 601 | updated: 'Actualizado el usuario "%username%"' |
564 | deleted: 'Eliminado el usuario "%username%"' | 602 | deleted: 'Eliminado el usuario "%username%"' |
603 | site_credential: | ||
604 | notice: | ||
605 | # added: 'Site credential for "%host%" added' | ||
606 | # updated: 'Site credential for "%host%" updated' | ||
607 | # 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..ffc48933 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | # filtered_tags: 'Filtered by tags:' | 169 | # filtered_tags: 'Filtered by tags:' |
165 | # filtered_search: 'Filtered by search:' | 170 | # filtered_search: 'Filtered by search:' |
166 | # untagged: 'Untagged entries' | 171 | # untagged: 'Untagged entries' |
172 | # all: 'All entries' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} هیج مقالهای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.' | 174 | number_on_the_page: '{0} هیج مقالهای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.' |
169 | reading_time: 'زمان تخمینی برای خواندن' | 175 | reading_time: 'زمان تخمینی برای خواندن' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'خواندهنشده' | 191 | unread_label: 'خواندهنشده' |
186 | preview_picture_label: 'دارای عکس پیشنمایش' | 192 | preview_picture_label: 'دارای عکس پیشنمایش' |
187 | preview_picture_help: 'پیشنمایش عکس' | 193 | preview_picture_help: 'پیشنمایش عکس' |
194 | # is_public_label: 'Has a public link' | ||
195 | # is_public_help: 'Public link' | ||
188 | language_label: 'زبان' | 196 | language_label: 'زبان' |
189 | # http_status_label: 'HTTP status' | 197 | # http_status_label: 'HTTP status' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'اصلی' | 231 | original_article: 'اصلی' |
224 | annotations_on_the_entry: '{0} بدون حاشیه|{1} یک حاشیه|]1,Inf[ %nbحاشیه% annotations' | 232 | annotations_on_the_entry: '{0} بدون حاشیه|{1} یک حاشیه|]1,Inf[ %nbحاشیه% annotations' |
225 | created_at: 'زمان ساخت' | 233 | created_at: 'زمان ساخت' |
234 | # published_at: 'Publication date' | ||
235 | # published_by: 'Published by' | ||
226 | new: | 236 | new: |
227 | page_title: 'ذخیرهٔ مقالهٔ تازه' | 237 | page_title: 'ذخیرهٔ مقالهٔ تازه' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: 'ویرایش مقاله' | 244 | page_title: 'ویرایش مقاله' |
235 | title_label: 'عنوان' | 245 | title_label: 'عنوان' |
236 | url_label: 'نشانی' | 246 | url_label: 'نشانی' |
237 | is_public_label: 'عمومی' | ||
238 | save_label: 'ذخیره' | 247 | save_label: 'ذخیره' |
239 | public: | 248 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 249 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | # delete: "Are you sure you want to remove that article?" | ||
252 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'درباره' | 255 | page_title: 'درباره' |
@@ -385,6 +397,9 @@ tag: | |||
385 | # add: 'Add' | 397 | # add: 'Add' |
386 | # placeholder: 'You can add several tags, separated by a comma.' | 398 | # placeholder: 'You can add several tags, separated by a comma.' |
387 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'درونریزی' | 404 | page_title: 'درونریزی' |
390 | page_description: 'به درونریز wallabag خوش آمدید. لطفاً سرویس قبلی خود را که میخواهید از آن مهاجرت کنید انتخاب کنید.' | 405 | page_description: 'به درونریز wallabag خوش آمدید. لطفاً سرویس قبلی خود را که میخواهید از آن مهاجرت کنید انتخاب کنید.' |
@@ -510,6 +525,28 @@ user: | |||
510 | # delete: Delete | 525 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 526 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 527 | # back_to_list: Back to list |
528 | search: | ||
529 | # placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | # page_title: Site credentials management | ||
533 | # new_site_credential: Create a credential | ||
534 | # edit_site_credential: Edit an existing credential | ||
535 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | # list: | ||
537 | # actions: Actions | ||
538 | # edit_action: Edit | ||
539 | # yes: Yes | ||
540 | # no: No | ||
541 | # create_new_one: Create a new credential | ||
542 | # form: | ||
543 | # username_label: 'Username' | ||
544 | # host_label: 'Host' | ||
545 | # password_label: 'Password' | ||
546 | # save: Save | ||
547 | # delete: Delete | ||
548 | # delete_confirm: Are you sure? | ||
549 | # back_to_list: Back to list | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | # page_title: An error occurred | 552 | # page_title: An error occurred |
@@ -528,6 +565,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 565 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 566 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 567 | # entries_reset: Entries reset |
568 | # archived_reset: Archived entries deleted | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' | 571 | entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 600 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 601 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 602 | # deleted: 'User "%username%" deleted' |
603 | site_credential: | ||
604 | notice: | ||
605 | # added: 'Site credential for "%host%" added' | ||
606 | # updated: 'Site credential for "%host%" updated' | ||
607 | # 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..c9d95e2b 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,8 +167,9 @@ 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" |
172 | all: "Tous les articles" | ||
167 | list: | 173 | 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." | 174 | 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." |
169 | reading_time: "durée de lecture" | 175 | reading_time: "durée de lecture" |
@@ -185,8 +191,10 @@ entry: | |||
185 | unread_label: "Non lus" | 191 | unread_label: "Non lus" |
186 | preview_picture_label: "A une photo" | 192 | preview_picture_label: "A une photo" |
187 | preview_picture_help: "Photo" | 193 | preview_picture_help: "Photo" |
194 | is_public_label: 'A un lien public' | ||
195 | is_public_help: 'Lien public' | ||
188 | language_label: "Langue" | 196 | language_label: "Langue" |
189 | http_status_label: 'Statut HTTP' | 197 | http_status_label: "Statut HTTP" |
190 | reading_time: | 198 | reading_time: |
191 | label: "Durée de lecture en minutes" | 199 | label: "Durée de lecture en minutes" |
192 | from: "de" | 200 | from: "de" |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: "original" | 231 | original_article: "original" |
224 | annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations" | 232 | annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations" |
225 | created_at: "Date de création" | 233 | created_at: "Date de création" |
234 | published_at: "Date de publication" | ||
235 | published_by: "Publié par" | ||
226 | new: | 236 | new: |
227 | page_title: "Sauvegarder un nouvel article" | 237 | page_title: "Sauvegarder un nouvel article" |
228 | placeholder: "http://website.com" | 238 | placeholder: "http://website.com" |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: "Éditer un article" | 244 | page_title: "Éditer un article" |
235 | title_label: "Titre" | 245 | title_label: "Titre" |
236 | url_label: "Adresse" | 246 | url_label: "Adresse" |
237 | is_public_label: "Public" | ||
238 | save_label: "Enregistrer" | 247 | save_label: "Enregistrer" |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "Cet article a été partagé par <a href=\"%wallabag_instance%\">wallabag</a>" | 249 | shared_by_wallabag: "Cet article a été partagé par %username% avec <a href=\"%wallabag_instance%\">wallabag</a>" |
250 | confirm: | ||
251 | delete: "Voulez-vous vraiment supprimer cet article ?" | ||
252 | delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: "À propos" | 255 | page_title: "À propos" |
@@ -295,32 +307,32 @@ howto: | |||
295 | bookmarklet: | 307 | bookmarklet: |
296 | description: "Glissez et déposez ce lien dans votre barre de favoris :" | 308 | description: "Glissez et déposez ce lien dans votre barre de favoris :" |
297 | shortcuts: | 309 | shortcuts: |
298 | page_description: Voici les raccourcis disponibles dans wallabag. | 310 | page_description: "Voici les raccourcis disponibles dans wallabag." |
299 | shortcut: Raccourci | 311 | shortcut: "Raccourci" |
300 | action: Action | 312 | action: "Action" |
301 | all_pages_title: Raccourcis disponibles dans toutes les pages | 313 | all_pages_title: "Raccourcis disponibles dans toutes les pages" |
302 | go_unread: Afficher les articles non lus | 314 | go_unread: "Afficher les articles non lus" |
303 | go_starred: Afficher les articles favoris | 315 | go_starred: "Afficher les articles favoris" |
304 | go_archive: Afficher les articles lus | 316 | go_archive: "Afficher les articles lus" |
305 | go_all: Afficher tous les articles | 317 | go_all: "Afficher tous les articles" |
306 | go_tags: Afficher les tags | 318 | go_tags: "Afficher les tags" |
307 | go_config: Aller à la configuration | 319 | go_config: "Aller à la configuration" |
308 | go_import: Aller aux imports | 320 | go_import: "Aller aux imports" |
309 | go_developers: Aller à la section Développeurs | 321 | go_developers: "Aller à la section Développeurs" |
310 | go_howto: Afficher l'aide (cette page !) | 322 | go_howto: "Afficher l’aide (cette page !)" |
311 | go_logout: Se déconnecter | 323 | go_logout: "Se déconnecter" |
312 | list_title: Raccourcis disponibles dans les pages de liste | 324 | list_title: "Raccourcis disponibles dans les pages de liste" |
313 | search: Afficher le formulaire de recherche | 325 | search: "Afficher le formulaire de recherche" |
314 | article_title: Raccourcis disponibles quand on affiche un article | 326 | article_title: "Raccourcis disponibles quand on affiche un article" |
315 | open_original: Ouvrir l'URL originale de l'article | 327 | open_original: "Ouvrir l’URL originale de l’article" |
316 | toggle_favorite: Changer le statut Favori de l'article | 328 | toggle_favorite: "Changer le statut Favori de l’article" |
317 | toggle_archive: Changer le status Lu de l'article | 329 | toggle_archive: "Changer le status Lu de l’article" |
318 | delete: Supprimer l'article | 330 | delete: "Supprimer l’article" |
319 | material_title: Raccourcis disponibles avec le thème Material uniquement | 331 | material_title: "Raccourcis disponibles avec le thème Material uniquement" |
320 | add_link: Ajouter un nouvel article | 332 | add_link: "Ajouter un nouvel article" |
321 | hide_form: Masquer le formulaire courant (recherche ou nouvel article) | 333 | hide_form: "Masquer le formulaire courant (recherche ou nouvel article)" |
322 | arrows_navigation: Naviguer à travers les articles | 334 | arrows_navigation: "Naviguer à travers les articles" |
323 | open_article: Afficher l'article sélectionné | 335 | open_article: "Afficher l’article sélectionné" |
324 | 336 | ||
325 | quickstart: | 337 | quickstart: |
326 | page_title: "Pour bien débuter" | 338 | page_title: "Pour bien débuter" |
@@ -382,8 +394,11 @@ 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." | 394 | 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" | 395 | see_untagged_entries: "Voir les articles sans tag" |
384 | new: | 396 | new: |
385 | add: 'Ajouter' | 397 | add: "Ajouter" |
386 | placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.' | 398 | placeholder: "Vous pouvez ajouter plusieurs tags, séparés par une virgule." |
399 | |||
400 | export: | ||
401 | footer_template: '<div style="text-align:center;"><p>Généré par wallabag with %method%</p><p>Merci d''ouvrir <a href="https://github.com/wallabag/wallabag/issues">un ticket</a> si vous rencontrez des soucis d''affichage avec ce document sur votre support.</p></div>' | ||
387 | 402 | ||
388 | import: | 403 | import: |
389 | page_title: "Importer" | 404 | page_title: "Importer" |
@@ -417,7 +432,7 @@ import: | |||
417 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." | 432 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." |
418 | worker: | 433 | 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 :" | 434 | 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." | 435 | 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: | 436 | firefox: |
422 | page_title: "Import > Firefox" | 437 | 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>" | 438 | 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 +501,16 @@ developer: | |||
486 | back: "Retour" | 501 | back: "Retour" |
487 | 502 | ||
488 | user: | 503 | user: |
489 | page_title: Gestion des utilisateurs | 504 | page_title: "Gestion des utilisateurs" |
490 | new_user: Créer un nouvel utilisateur | 505 | new_user: "Créer un nouvel utilisateur" |
491 | edit_user: Éditer un utilisateur existant | 506 | edit_user: "Éditer un utilisateur existant" |
492 | description: Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression) | 507 | description: "Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression)" |
493 | list: | 508 | list: |
494 | actions: Actions | 509 | actions: "Actions" |
495 | edit_action: Éditer | 510 | edit_action: "Éditer" |
496 | yes: Oui | 511 | yes: "Oui" |
497 | no: Non | 512 | no: "Non" |
498 | create_new_one: Créer un nouvel utilisateur | 513 | create_new_one: "Créer un nouvel utilisateur" |
499 | form: | 514 | form: |
500 | username_label: "Nom d’utilisateur" | 515 | username_label: "Nom d’utilisateur" |
501 | name_label: "Nom" | 516 | name_label: "Nom" |
@@ -508,11 +523,33 @@ user: | |||
508 | twofactor_label: "Double authentification" | 523 | twofactor_label: "Double authentification" |
509 | save: "Sauvegarder" | 524 | save: "Sauvegarder" |
510 | delete: "Supprimer" | 525 | delete: "Supprimer" |
511 | delete_confirm: "Voulez-vous vraiment ?" | 526 | delete_confirm: "Êtes-vous sûr ?" |
527 | back_to_list: "Revenir à la liste" | ||
528 | search: | ||
529 | placeholder: "Filtrer par nom d’utilisateur ou email" | ||
530 | |||
531 | site_credential: | ||
532 | page_title: Gestion des accès aux sites | ||
533 | new_site_credential: Créer un accès à un site | ||
534 | edit_site_credential: Éditer l'accès d'un site | ||
535 | 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" | ||
536 | list: | ||
537 | actions: Actions | ||
538 | edit_action: Éditer | ||
539 | yes: Oui | ||
540 | no: Non | ||
541 | create_new_one: Créer un nouvel accès à un site | ||
542 | form: | ||
543 | username_label: 'Identifiant' | ||
544 | host_label: 'Domaine' | ||
545 | password_label: 'Mot de passe' | ||
546 | save: "Sauvegarder" | ||
547 | delete: "Supprimer" | ||
548 | delete_confirm: "Êtes-vous sûr ?" | ||
512 | back_to_list: "Revenir à la liste" | 549 | back_to_list: "Revenir à la liste" |
513 | 550 | ||
514 | error: | 551 | error: |
515 | page_title: Une erreur est survenue | 552 | page_title: "Une erreur est survenue" |
516 | 553 | ||
517 | flashes: | 554 | flashes: |
518 | config: | 555 | config: |
@@ -525,9 +562,10 @@ flashes: | |||
525 | tagging_rules_updated: "Règles mises à jour" | 562 | tagging_rules_updated: "Règles mises à jour" |
526 | tagging_rules_deleted: "Règle supprimée" | 563 | tagging_rules_deleted: "Règle supprimée" |
527 | rss_token_updated: "Jeton RSS mis à jour" | 564 | rss_token_updated: "Jeton RSS mis à jour" |
528 | annotations_reset: Annotations supprimées | 565 | annotations_reset: "Annotations supprimées" |
529 | tags_reset: Tags supprimés | 566 | tags_reset: "Tags supprimés" |
530 | entries_reset: Articles supprimés | 567 | entries_reset: "Articles supprimés" |
568 | archived_reset: "Articles archivés supprimés" | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: "Article déjà sauvegardé le %date%" | 571 | entry_already_saved: "Article déjà sauvegardé le %date%" |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'Utilisateur "%username%" ajouté' | 600 | added: 'Utilisateur "%username%" ajouté' |
563 | updated: 'Utilisateur "%username%" mis à jour' | 601 | updated: 'Utilisateur "%username%" mis à jour' |
564 | deleted: 'Utilisateur "%username%" supprimé' | 602 | deleted: 'Utilisateur "%username%" supprimé' |
603 | site_credential: | ||
604 | notice: | ||
605 | added: 'Accès au site "%host%" ajouté' | ||
606 | updated: 'Accès au site "%host%" mis à jour' | ||
607 | 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..c53266ca 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: |
@@ -166,6 +169,7 @@ entry: | |||
166 | filtered_tags: 'Filtrati per etichetta:' | 169 | filtered_tags: 'Filtrati per etichetta:' |
167 | filtered_search: 'Filtrati per ricerca:' | 170 | filtered_search: 'Filtrati per ricerca:' |
168 | untagged: 'Articoli non etichettati' | 171 | untagged: 'Articoli non etichettati' |
172 | all: 'Tutti gli articoli' | ||
169 | list: | 173 | list: |
170 | number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti." | 174 | number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti." |
171 | reading_time: 'tempo di lettura stimato' | 175 | reading_time: 'tempo di lettura stimato' |
@@ -187,6 +191,8 @@ entry: | |||
187 | unread_label: 'Non letti' | 191 | unread_label: 'Non letti' |
188 | preview_picture_label: "Ha un'immagine di anteprima" | 192 | preview_picture_label: "Ha un'immagine di anteprima" |
189 | preview_picture_help: 'Immagine di anteprima' | 193 | preview_picture_help: 'Immagine di anteprima' |
194 | # is_public_label: 'Has a public link' | ||
195 | # is_public_help: 'Public link' | ||
190 | language_label: 'Lingua' | 196 | language_label: 'Lingua' |
191 | http_status_label: 'Stato HTTP' | 197 | http_status_label: 'Stato HTTP' |
192 | reading_time: | 198 | reading_time: |
@@ -211,7 +217,7 @@ entry: | |||
211 | view_original_article: 'Contenuto originale' | 217 | view_original_article: 'Contenuto originale' |
212 | re_fetch_content: 'Ri-ottieni pagina' | 218 | re_fetch_content: 'Ri-ottieni pagina' |
213 | delete: 'Elimina' | 219 | delete: 'Elimina' |
214 | add_a_tag: 'Aggiungi un tag' | 220 | add_a_tag: 'Aggiungi un''etichetta' |
215 | share_content: 'Condividi' | 221 | share_content: 'Condividi' |
216 | share_email_label: 'E-mail' | 222 | share_email_label: 'E-mail' |
217 | public_link: 'Link pubblico' | 223 | public_link: 'Link pubblico' |
@@ -222,7 +228,7 @@ entry: | |||
222 | label: 'Problemi?' | 228 | label: 'Problemi?' |
223 | description: 'Questo contenuto viene visualizzato male?' | 229 | description: 'Questo contenuto viene visualizzato male?' |
224 | edit_title: 'Modifica titolo' | 230 | edit_title: 'Modifica titolo' |
225 | original_article: 'originale' | 231 | original_article: 'Originale' |
226 | annotations_on_the_entry: '{0} Nessuna annotazione|{1} Una annotazione|]1,Inf[ %count% annotazioni' | 232 | annotations_on_the_entry: '{0} Nessuna annotazione|{1} Una annotazione|]1,Inf[ %count% annotazioni' |
227 | created_at: 'Data di creazione' | 233 | created_at: 'Data di creazione' |
228 | published_at: 'Data di pubblicazione' | 234 | published_at: 'Data di pubblicazione' |
@@ -238,13 +244,15 @@ entry: | |||
238 | page_title: 'Modifica voce' | 244 | page_title: 'Modifica voce' |
239 | title_label: 'Titolo' | 245 | title_label: 'Titolo' |
240 | url_label: 'Url' | 246 | url_label: 'Url' |
241 | is_public_label: 'Pubblico' | ||
242 | save_label: 'Salva' | 247 | save_label: 'Salva' |
243 | public: | 248 | public: |
244 | shared_by_wallabag: "Questo articolo è stato condiviso da %username% con <a href='%wallabag_instance%'>wallabag</a>" | 249 | shared_by_wallabag: "Questo articolo è stato condiviso da %username% con <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | delete: "Vuoi veramente rimuovere quell'articolo?" | ||
252 | delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?" | ||
245 | 253 | ||
246 | about: | 254 | about: |
247 | page_title: 'About' | 255 | page_title: 'A proposito' |
248 | top_menu: | 256 | top_menu: |
249 | who_behind_wallabag: "Chi c'è dietro a wallabag" | 257 | who_behind_wallabag: "Chi c'è dietro a wallabag" |
250 | getting_help: 'Ottieni aiuto' | 258 | getting_help: 'Ottieni aiuto' |
@@ -263,7 +271,7 @@ about: | |||
263 | bug_reports: 'Bug reports' | 271 | bug_reports: 'Bug reports' |
264 | support: '<a href="https://github.com/wallabag/wallabag/issues">su GitHub</a>' | 272 | support: '<a href="https://github.com/wallabag/wallabag/issues">su GitHub</a>' |
265 | helping: | 273 | helping: |
266 | description: 'wallabag è gratuito opensource. Puoi aiutarci:' | 274 | description: 'wallabag è gratuito ed OpenSource. Puoi aiutarci:' |
267 | by_contributing: 'per contribuire al progetto:' | 275 | by_contributing: 'per contribuire al progetto:' |
268 | by_contributing_2: 'un elenco delle attività richieste' | 276 | by_contributing_2: 'un elenco delle attività richieste' |
269 | by_paypal: 'via Paypal' | 277 | by_paypal: 'via Paypal' |
@@ -331,7 +339,7 @@ quickstart: | |||
331 | more: 'Più…' | 339 | more: 'Più…' |
332 | intro: | 340 | intro: |
333 | title: 'Benvenuto su wallabag!' | 341 | title: 'Benvenuto su wallabag!' |
334 | paragraph_1: "Un tour in cui ti guideremo per scoprire e che ti mostrerà delle funzionalità che potrebbero interessarti." | 342 | paragraph_1: "Ti accompagneremo alla scoperta di wallabag e ti mostreremo delle funzionalità che potrebbero interessarti." |
335 | paragraph_2: 'Seguici!' | 343 | paragraph_2: 'Seguici!' |
336 | configure: | 344 | configure: |
337 | title: "Configura l'applicazione" | 345 | title: "Configura l'applicazione" |
@@ -389,6 +397,9 @@ tag: | |||
389 | add: 'Aggiungi' | 397 | add: 'Aggiungi' |
390 | placeholder: 'Puoi aggiungere varie etichette, separate da una virgola.' | 398 | placeholder: 'Puoi aggiungere varie etichette, separate da una virgola.' |
391 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
392 | import: | 403 | import: |
393 | page_title: 'Importa' | 404 | page_title: 'Importa' |
394 | page_description: "Benvenuto nell'importatore di wallabag. Seleziona il servizio da cui vuoi trasferire i contenuti." | 405 | page_description: "Benvenuto nell'importatore di wallabag. Seleziona il servizio da cui vuoi trasferire i contenuti." |
@@ -401,20 +412,20 @@ import: | |||
401 | save_label: 'Carica file' | 412 | save_label: 'Carica file' |
402 | pocket: | 413 | pocket: |
403 | page_title: 'Importa da > Pocket' | 414 | 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." | 415 | 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: | 416 | config_missing: |
406 | description: "Importazione da Pocket non configurata." | 417 | description: "Importazione da Pocket non configurata." |
407 | admin_message: 'Devi definire %keyurls% una pocket_consumer_key %keyurle%.' | 418 | 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.' | 419 | 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.' | 420 | 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' | 421 | connect_to_pocket: 'Connetti a Pocket and importa i dati' |
411 | wallabag_v1: | 422 | wallabag_v1: |
412 | page_title: 'Importa da > Wallabag v1' | 423 | 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".' | 424 | 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.' | 425 | how_to: 'Seleziona la tua esportazione di wallabag e clicca sul pulsante sottostante caricare il file e importare i dati.' |
415 | wallabag_v2: | 426 | wallabag_v2: |
416 | page_title: 'Importa da > Wallabag v2' | 427 | 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".' | 428 | 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: | 429 | readability: |
419 | page_title: 'Importa da > Readability' | 430 | 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).' | 431 | 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 +525,28 @@ user: | |||
514 | delete: Cancella | 525 | delete: Cancella |
515 | delete_confirm: Sei sicuro? | 526 | delete_confirm: Sei sicuro? |
516 | back_to_list: Torna alla lista | 527 | back_to_list: Torna alla lista |
528 | search: | ||
529 | # placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | # page_title: Site credentials management | ||
533 | # new_site_credential: Create a credential | ||
534 | # edit_site_credential: Edit an existing credential | ||
535 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | # list: | ||
537 | # actions: Actions | ||
538 | # edit_action: Edit | ||
539 | # yes: Yes | ||
540 | # no: No | ||
541 | # create_new_one: Create a new credential | ||
542 | # form: | ||
543 | # username_label: 'Username' | ||
544 | # host_label: 'Host' | ||
545 | # password_label: 'Password' | ||
546 | # save: Save | ||
547 | # delete: Delete | ||
548 | # delete_confirm: Are you sure? | ||
549 | # back_to_list: Back to list | ||
517 | 550 | ||
518 | error: | 551 | error: |
519 | page_title: Si è verificato un errore | 552 | page_title: Si è verificato un errore |
@@ -532,6 +565,7 @@ flashes: | |||
532 | annotations_reset: Reset annotazioni | 565 | annotations_reset: Reset annotazioni |
533 | tags_reset: Reset etichette | 566 | tags_reset: Reset etichette |
534 | entries_reset: Reset articoli | 567 | entries_reset: Reset articoli |
568 | # archived_reset: Archived entries deleted | ||
535 | entry: | 569 | entry: |
536 | notice: | 570 | notice: |
537 | entry_already_saved: 'Contenuto già salvato in data %date%' | 571 | entry_already_saved: 'Contenuto già salvato in data %date%' |
@@ -566,3 +600,8 @@ flashes: | |||
566 | added: 'Utente "%username%" aggiunto' | 600 | added: 'Utente "%username%" aggiunto' |
567 | updated: 'Utente "%username%" aggiornato' | 601 | updated: 'Utente "%username%" aggiornato' |
568 | deleted: 'Utente "%username%" eliminato' | 602 | deleted: 'Utente "%username%" eliminato' |
603 | site_credential: | ||
604 | notice: | ||
605 | # added: 'Site credential for "%host%" added' | ||
606 | # updated: 'Site credential for "%host%" updated' | ||
607 | # 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..3ae64c49 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml | |||
@@ -1,8 +1,8 @@ | |||
1 | security: | 1 | security: |
2 | login: | 2 | login: |
3 | page_title: 'Benvenguda sus wallabag !' | 3 | page_title: 'Vos desirem la benvenguda a wallabag !' |
4 | keep_logged_in: 'Demorar connectat' | 4 | keep_logged_in: 'Demorar connectat' |
5 | forgot_password: 'Senhal doblidat ?' | 5 | forgot_password: 'Senhal oblidat ?' |
6 | submit: 'Se connectar' | 6 | submit: 'Se connectar' |
7 | register: 'Crear un compte' | 7 | register: 'Crear un compte' |
8 | username: "Nom d'utilizaire" | 8 | username: "Nom d'utilizaire" |
@@ -11,8 +11,8 @@ security: | |||
11 | resetting: | 11 | resetting: |
12 | description: "Picatz vòstra adreça de corrièl çai-jos, vos mandarem las instruccions per reïnicializar vòstre senhal." | 12 | description: "Picatz vòstra adreça de corrièl çai-jos, vos mandarem las instruccions per reïnicializar vòstre senhal." |
13 | register: | 13 | register: |
14 | page_title: 'Se crear un compte' | 14 | page_title: 'Crear un compte' |
15 | go_to_account: 'Anar sus vòstre compte' | 15 | go_to_account: 'Anar a vòstre compte' |
16 | 16 | ||
17 | menu: | 17 | menu: |
18 | left: | 18 | left: |
@@ -22,7 +22,7 @@ menu: | |||
22 | all_articles: 'Totes los articles' | 22 | all_articles: 'Totes los articles' |
23 | config: 'Configuracion' | 23 | config: 'Configuracion' |
24 | tags: 'Etiquetas' | 24 | tags: 'Etiquetas' |
25 | internal_settings: 'Configuracion interna' | 25 | internal_settings: 'Configuracion intèrna' |
26 | import: 'Importar' | 26 | import: 'Importar' |
27 | howto: 'Ajuda' | 27 | howto: 'Ajuda' |
28 | developer: 'Gestion dels clients API' | 28 | developer: 'Gestion dels clients API' |
@@ -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: 'Identificants del site' | ||
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' |
@@ -46,7 +47,7 @@ footer: | |||
46 | social: 'Social' | 47 | social: 'Social' |
47 | powered_by: 'propulsat per' | 48 | powered_by: 'propulsat per' |
48 | about: 'A prepaus' | 49 | about: 'A prepaus' |
49 | stats: "Dempuèi %user_creation% avètz legit %nb_archives% articles. Es a l'entorn de %per_day% per jorn !" | 50 | stats: "Dempuèi %user_creation% avètz legit %nb_archives% articles. Es a l'entorn de %per_day% per jorn !" |
50 | 51 | ||
51 | config: | 52 | config: |
52 | page_title: 'Configuracion' | 53 | page_title: 'Configuracion' |
@@ -65,17 +66,18 @@ config: | |||
65 | language_label: 'Lenga' | 66 | language_label: 'Lenga' |
66 | reading_speed: | 67 | reading_speed: |
67 | label: 'Velocitat de lectura' | 68 | label: 'Velocitat de lectura' |
68 | help_message: 'Podètz utilizar una aisina en linha per estimar vòstra velocitat de lectura :' | 69 | help_message: 'Podètz utilizar una aisina en linha per estimar vòstra velocitat de lectura :' |
69 | 100_word: "Legissi a l'entorn de 100 mots per minuta" | 70 | 100_word: "Legissi a l'entorn de 100 mots per minuta" |
70 | 200_word: "Legissi a l'entorn de 200 mots per minuta" | 71 | 200_word: "Legissi a l'entorn de 200 mots per minuta" |
71 | 300_word: "Legissi a l'entorn de 300 mots per minuta" | 72 | 300_word: "Legissi a l'entorn de 300 mots per minuta" |
72 | 400_word: "Legissi a l'entorn de 400 mots per minuta" | 73 | 400_word: "Legissi a l'entorn de 400 mots per minuta" |
73 | action_mark_as_read: | 74 | action_mark_as_read: |
74 | label: 'Ont volètz èsser menat aprèp aver marcat un article coma legit ?' | 75 | label: 'Ont volètz èsser menat aprèp aver marcat un article coma legit ?' |
75 | redirect_homepage: "A la pagina d'acuèlh" | 76 | redirect_homepage: "A la pagina d’acuèlh" |
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: "Tocatz aquí per garnir las informacions de l'aplicacion Android" | ||
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,22 +89,23 @@ 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: 'Totes' | ||
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 en dos temps vòl dire que recebretz un còdi per corrièl per cada novèla connexion pas aprovada." |
98 | name_label: 'Nom' | 101 | name_label: 'Nom' |
99 | email_label: 'Adreça de corrièl' | 102 | email_label: 'Adreça de corrièl' |
100 | twoFactorAuthentication_label: 'Dobla autentificacion' | 103 | twoFactorAuthentication_label: 'Dobla autentificacion' |
101 | help_twoFactorAuthentication: "S'avètz activat 2FA, cada còp que volètz vos connectar a wallabag, recebretz un còdi per corrièl." | 104 | help_twoFactorAuthentication: "S'avètz activat l'autentificacion en dos temps, cada còp que volètz vos connectar a wallabag, recebretz un còdi per corrièl." |
102 | delete: | 105 | delete: |
103 | title: Suprimir mon compte (Mèfi zòna perilhosa) | 106 | title: Suprimir mon compte (Mèfi zòna perilhosa) |
104 | description: Se confirmatz la supression de vòstre compte, TOTES vòstres articles, TOTAS vòstras etiquetas, TOTAS vòstras anotacions e vòstre compte seràn suprimits per totjorn. E aquò es IRREVERSIBLE. Puèi seretz desconnectat. | 107 | description: Se confirmatz la supression de vòstre compte, TOTES vòstres articles, TOTAS vòstras etiquetas, TOTAS vòstras anotacions e vòstre compte seràn suprimits per totjorn. E aquò es IRREVERSIBLE. Puèi seretz desconnectat. |
105 | confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE) | 108 | confirm: Sètz vertadièrament segur ? (AQUÒ ES IRREVERSIBLE) |
106 | button: Suprimir mon compte | 109 | button: Suprimir mon compte |
107 | reset: | 110 | reset: |
108 | title: Zòna de reïnicializacion (Mèfi zòna perilhosa) | 111 | title: Zòna de reïnicializacion (Mèfi zòna perilhosa) |
@@ -110,7 +113,8 @@ 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 |
113 | confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE) | 116 | archived: Levar TOTES los articles archivats |
117 | confirm: Sètz vertadièrament segur ? (AQUÒ 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." |
116 | old_password_label: 'Senhal actual' | 120 | old_password_label: 'Senhal actual' |
@@ -125,12 +129,12 @@ config: | |||
125 | tags_label: 'Etiquetas' | 129 | tags_label: 'Etiquetas' |
126 | faq: | 130 | faq: |
127 | title: 'FAQ' | 131 | title: 'FAQ' |
128 | tagging_rules_definition_title: "Qué significa las règlas d'etiquetas automaticas ?" | 132 | tagging_rules_definition_title: "Qué significa las règlas d'etiquetas automaticas ?" |
129 | tagging_rules_definition_description: "Son de règlas utilizadas per wallabad per classar automaticament vòstres novèls articles.<br />Cada còp qu'un novèl article es apondut, totas las règlas d'etiquetas automaticas seràn utilizadas per ajustar d'etiquetas qu'avètz configuradas, en vos esparnhant l'esfòrç de classificar vòstres articles manualament." | 133 | tagging_rules_definition_description: "Son de règlas utilizadas per wallabad per classar automaticament vòstres novèls articles.<br />Cada còp qu'un novèl article es apondut, totas las règlas d'etiquetas automaticas seràn utilizadas per ajustar d'etiquetas qu'avètz configuradas, en vos esparnhant l'esfòrç de classificar vòstres articles manualament." |
130 | how_to_use_them_title: 'Cossí las utilizar ?' | 134 | how_to_use_them_title: 'Cossí las utilizar ?' |
131 | how_to_use_them_description: "Imaginem que volètz atribuir als novèls article l'etiqueta « <i>lectura corta</i> » quand lo temps per legir es inferior a 3 minutas.<br />Dins aquel cas, deuriatz metre « readingTime <= 3 » dins lo camp <i>Règla</i> e « <i>lectura corta</i> » dins lo camp <i>Etiqueta</i>.<br />Mai d'una etiquetas pòdon èsser apondudas simultanèament ne las separant amb de virgulas : « <i>lectura corta, per ligir</i> »<br />De règlas complèxas pòdon èsser creadas en emplegant d'operators predefinits : se « <i>readingTime >= 5 AND domainName = \"github.com\"</i> » alara atribuir las etiquetas « <i>lectura longa, github </i> »" | 135 | how_to_use_them_description: "Imaginem que volètz atribuir als novèls article l'etiqueta « <i>lectura corta</i> » quand lo temps per legir es inferior a 3 minutas.<br />Dins aquel cas, deuriatz metre « readingTime <= 3 » dins lo camp <i>Règla</i> e « <i>lectura corta</i> » dins lo camp <i>Etiqueta</i>.<br />Mai d'una etiquetas pòdon èsser apondudas simultanèament ne las separant amb de virgulas : « <i>lectura corta, per ligir</i> »<br />De règlas complèxas pòdon èsser creadas n'emplegant d'operators predefinits : se « <i>readingTime >= 5 AND domainName = \"github.com\"</i> » alara atribuir las etiquetas « <i>lectura longa, github </i> »" |
132 | variables_available_title: 'Quinas variablas e operators pòdi utilizar per escriure de règlas ?' | 136 | variables_available_title: 'Quinas variablas e operators pòdi utilizar per escriure de règlas ?' |
133 | variables_available_description: "Las variablas e operators seguents pòdon èsser utilizats per escriure de règlas d'etiquetas automaticas :" | 137 | variables_available_description: "Las variablas e operators seguents pòdon èsser utilizats per escriure de règlas d'etiquetas automaticas : " |
134 | meaning: 'Significacion' | 138 | meaning: 'Significacion' |
135 | variable_description: | 139 | variable_description: |
136 | label: 'Variabla' | 140 | label: 'Variabla' |
@@ -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: |
@@ -161,14 +166,15 @@ entry: | |||
161 | starred: 'Articles favorits' | 166 | starred: 'Articles favorits' |
162 | archived: 'Articles legits' | 167 | archived: 'Articles legits' |
163 | filtered: 'Articles filtrats' | 168 | filtered: 'Articles filtrats' |
164 | filtered_tags: 'Articles filtrats per etiquetas :' | 169 | filtered_tags: 'Articles filtrats per etiquetas :' |
165 | filtered_search: 'Articles filtrats per recèrca :' | 170 | filtered_search: 'Articles filtrats per recèrca :' |
166 | untagged: 'Articles sens etiqueta' | 171 | untagged: 'Articles sens etiqueta' |
172 | all: 'Totes los articles' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles." | 174 | number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles." |
169 | reading_time: 'durada de lectura' | 175 | reading_time: 'durada de lectura' |
170 | reading_time_minutes: 'durada de lectura : %readingTime% min' | 176 | reading_time_minutes: 'durada de lectura : %readingTime% min' |
171 | reading_time_less_one_minute: 'durada de lectura : < 1 min' | 177 | reading_time_less_one_minute: 'durada de lectura : < 1 min' |
172 | number_of_tags: '{1}e una autra etiqueta|]1,Inf[e %count% autras etiquetas' | 178 | number_of_tags: '{1}e una autra etiqueta|]1,Inf[e %count% autras etiquetas' |
173 | reading_time_minutes_short: '%readingTime% min' | 179 | reading_time_minutes_short: '%readingTime% min' |
174 | reading_time_less_one_minute_short: '< 1 min' | 180 | reading_time_less_one_minute_short: '< 1 min' |
@@ -183,8 +189,10 @@ entry: | |||
183 | archived_label: 'Legits' | 189 | archived_label: 'Legits' |
184 | starred_label: 'Favorits' | 190 | starred_label: 'Favorits' |
185 | unread_label: 'Pas legits' | 191 | unread_label: 'Pas legits' |
186 | preview_picture_label: 'A una fotò' | 192 | preview_picture_label: 'A un imatge' |
187 | preview_picture_help: 'Fotò' | 193 | preview_picture_help: 'Imatge' |
194 | is_public_label: 'Ten un ligam public' | ||
195 | is_public_help: 'Ligam public' | ||
188 | language_label: 'Lenga' | 196 | language_label: 'Lenga' |
189 | http_status_label: 'Estatut HTTP' | 197 | http_status_label: 'Estatut HTTP' |
190 | reading_time: | 198 | reading_time: |
@@ -205,7 +213,7 @@ entry: | |||
205 | back_to_homepage: 'Tornar' | 213 | back_to_homepage: 'Tornar' |
206 | set_as_read: 'Marcar coma legit' | 214 | set_as_read: 'Marcar coma legit' |
207 | set_as_unread: 'Marcar coma pas legit' | 215 | set_as_unread: 'Marcar coma pas legit' |
208 | set_as_starred: 'Metre en favori' | 216 | set_as_starred: 'Metre en favorit' |
209 | view_original_article: 'Article original' | 217 | view_original_article: 'Article original' |
210 | re_fetch_content: 'Tornar cargar lo contengut' | 218 | re_fetch_content: 'Tornar cargar lo contengut' |
211 | delete: 'Suprimir' | 219 | delete: 'Suprimir' |
@@ -217,27 +225,31 @@ entry: | |||
217 | export: 'Exportar' | 225 | export: 'Exportar' |
218 | print: 'Imprimir' | 226 | print: 'Imprimir' |
219 | problem: | 227 | problem: |
220 | label: 'Un problèma ?' | 228 | label: 'Un problèma ?' |
221 | description: "Marca mal la presentacion d'aqueste article ?" | 229 | description: "Marca mal la presentacion d'aqueste article ?" |
222 | edit_title: 'Modificar lo títol' | 230 | edit_title: 'Modificar lo títol' |
223 | original_article: 'original' | 231 | original_article: 'original' |
224 | annotations_on_the_entry: "{0} Pas cap d'anotacion|{1} Una anotacion|]1,Inf[ %count% anotacions" | 232 | annotations_on_the_entry: "{0} Pas cap d'anotacion|{1} Una anotacion|]1,Inf[ %count% anotacions" |
225 | created_at: 'Data de creacion' | 233 | created_at: 'Data de creacion' |
234 | published_at: 'Data de publicacion' | ||
235 | published_by: 'Publicat per' | ||
226 | new: | 236 | new: |
227 | page_title: 'Enregistrar un novèl article' | 237 | page_title: 'Enregistrar un novèl article' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
229 | form_new: | 239 | form_new: |
230 | url_label: Url | 240 | url_label: Url |
231 | search: | 241 | search: |
232 | placeholder: 'Qué cercatz ?' | 242 | placeholder: 'Qué cercatz ?' |
233 | edit: | 243 | edit: |
234 | page_title: 'Modificar un article' | 244 | page_title: 'Modificar un article' |
235 | title_label: 'Títol' | 245 | title_label: 'Títol' |
236 | url_label: 'Url' | 246 | url_label: 'Url' |
237 | is_public_label: 'Public' | ||
238 | save_label: 'Enregistrar' | 247 | save_label: 'Enregistrar' |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>" | 249 | shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | delete: "Sètz segur de voler suprimir aqueste article ?" | ||
252 | delete_tag: "Sètz segur de voler levar aquesta etiqueta de l'article ?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'A prepaus' | 255 | page_title: 'A prepaus' |
@@ -259,27 +271,27 @@ about: | |||
259 | bug_reports: 'Rapòrt de bugs' | 271 | bug_reports: 'Rapòrt de bugs' |
260 | support: "<a href=\"https://github.com/wallabag/wallabag/issues\">sur GitHub</a>" | 272 | support: "<a href=\"https://github.com/wallabag/wallabag/issues\">sur GitHub</a>" |
261 | helping: | 273 | helping: |
262 | description: 'wallabag es a gratuit e opensource. Nos podètz ajudar :' | 274 | description: 'wallabag es a gratuit e opensource. Nos podètz ajudar : ' |
263 | by_contributing: 'en ajudant lo projècte :' | 275 | by_contributing: 'en participant lo projècte : ' |
264 | by_contributing_2: 'un bilhet recensa totes nòstres besonhs' | 276 | by_contributing_2: 'un bilhet recensa totes nòstres besonhs' |
265 | by_paypal: 'via Paypal' | 277 | by_paypal: 'via Paypal' |
266 | contributors: | 278 | contributors: |
267 | description: "Mercés als contributors de l'aplicacion web de wallabag" | 279 | description: "Mercés als contributors de l'aplicacion web de wallabag" |
268 | third_party: | 280 | third_party: |
269 | description: 'Aquí la lista de las dependéncias utilizadas dins wallabag (e lor licéncia) :' | 281 | description: 'Aquí la lista de las dependéncias utilizadas dins wallabag (e lor licéncia) :' |
270 | package: 'Dependéncia' | 282 | package: 'Dependéncia' |
271 | license: 'Licéncia' | 283 | license: 'Licéncia' |
272 | 284 | ||
273 | howto: | 285 | howto: |
274 | page_title: 'Ajuda' | 286 | page_title: 'Ajuda' |
275 | page_description: "I a mai d'un biais d'enregistrar un article :" | 287 | page_description: "I a mai d'un biais d'enregistrar un article :" |
276 | tab_menu: | 288 | tab_menu: |
277 | add_link: "Ajustar un ligam" | 289 | add_link: "Ajustar un ligam" |
278 | shortcuts: "Utilizar d'acorchis" | 290 | shortcuts: "Utilizar d'acorchis" |
279 | top_menu: | 291 | top_menu: |
280 | browser_addons: 'Extensions de navigator' | 292 | browser_addons: 'Extensions de navigator' |
281 | mobile_apps: 'Aplicacions mobil' | 293 | mobile_apps: 'Aplicacions mobil' |
282 | bookmarklet: 'Bookmarklet' | 294 | bookmarklet: 'Marcapaginas' |
283 | form: | 295 | form: |
284 | description: 'Gràcias a aqueste formulari' | 296 | description: 'Gràcias a aqueste formulari' |
285 | browser_addons: | 297 | browser_addons: |
@@ -293,7 +305,7 @@ howto: | |||
293 | ios: 'sus iTunes Store' | 305 | ios: 'sus iTunes Store' |
294 | windows: 'sus Microsoft Store' | 306 | windows: 'sus Microsoft Store' |
295 | bookmarklet: | 307 | bookmarklet: |
296 | description: 'Lisatz-depausatz aqueste ligam dins vòstra barra de favorits :' | 308 | description: 'Lisatz-depausatz aqueste ligam dins vòstra barra de favorits :' |
297 | shortcuts: | 309 | shortcuts: |
298 | page_description: Aquí son los acorchis disponibles dins wallabag. | 310 | page_description: Aquí son los acorchis disponibles dins wallabag. |
299 | shortcut: Acorchis | 311 | shortcut: Acorchis |
@@ -307,7 +319,7 @@ howto: | |||
307 | go_config: Anar a la config | 319 | go_config: Anar a la config |
308 | go_import: Anar per importar | 320 | go_import: Anar per importar |
309 | go_developers: Anar al canton desvolopaires | 321 | go_developers: Anar al canton desvolopaires |
310 | go_howto: Anar a l'ajuda (aquesta quita pagina !) | 322 | go_howto: Anar a l'ajuda (aquesta quita pagina !) |
311 | go_logout: Desconnexion | 323 | go_logout: Desconnexion |
312 | list_title: Acorchis disponibles dins las paginas de lista | 324 | list_title: Acorchis disponibles dins las paginas de lista |
313 | search: Afichar lo formulari de recèrca | 325 | search: Afichar lo formulari de recèrca |
@@ -326,9 +338,9 @@ quickstart: | |||
326 | page_title: 'Per ben començar' | 338 | page_title: 'Per ben començar' |
327 | more: 'Mai…' | 339 | more: 'Mai…' |
328 | intro: | 340 | intro: |
329 | title: 'Benvenguda sus wallabag !' | 341 | title: 'Benvenguda sus wallabag !' |
330 | paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina." | 342 | paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas foncionalitats que vos poirián interessar per vos apropriar aquesta aisina." |
331 | paragraph_2: 'Seguètz-nos !' | 343 | paragraph_2: 'Seguètz-nos !' |
332 | configure: | 344 | configure: |
333 | title: "Configuratz l'aplicacion" | 345 | title: "Configuratz l'aplicacion" |
334 | description: "Per fin d'aver una aplicacion que vos va ben, anatz veire la configuracion de wallabag." | 346 | description: "Per fin d'aver una aplicacion que vos va ben, anatz veire la configuracion de wallabag." |
@@ -337,20 +349,20 @@ quickstart: | |||
337 | tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' | 349 | tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' |
338 | admin: | 350 | admin: |
339 | title: 'Administracion' | 351 | title: 'Administracion' |
340 | description: "En qualitat d'adminitrastor sus wallabag, avètz de privilègis que vos permeton de :" | 352 | description: "En qualitat d'adminitrastor sus wallabag, avètz de privilègis que vos permeton de :" |
341 | new_user: 'Crear un novèl utilizaire' | 353 | new_user: 'Crear un novèl utilizaire' |
342 | analytics: 'Configurar las estadisticas' | 354 | analytics: 'Configurar las estadisticas' |
343 | sharing: 'Activar de paramètres de partatge' | 355 | sharing: 'Activar de paramètres de partatge' |
344 | export: 'Configurar los expòrt' | 356 | export: 'Configurar los expòrts' |
345 | import: 'Configurar los impòrt' | 357 | import: 'Configurar los impòrts' |
346 | first_steps: | 358 | first_steps: |
347 | title: 'Primièrs passes' | 359 | 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." | 360 | 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." |
349 | new_article: 'Ajustatz vòstre primièr article' | 361 | new_article: 'Ajustatz vòstre primièr article' |
350 | unread_articles: 'E racaptatz-lo !' | 362 | unread_articles: 'E recaptatz-lo !' |
351 | migrate: | 363 | migrate: |
352 | title: 'Migrar dempuèi un servici existent' | 364 | title: 'Migrar dempuèi un servici existent' |
353 | description: "Sètz un ancian utilizaire d'un servici existent ? Vos ajudarem a trapar vòstras donadas sus wallabag." | 365 | description: "Sètz un ancian utilizaire d'un servici existent ? Vos ajudarem a trapar vòstras donadas sus wallabag." |
354 | pocket: 'Migrar dempuèi Pocket' | 366 | pocket: 'Migrar dempuèi Pocket' |
355 | wallabag_v1: 'Migrar dempuèi wallabag v1' | 367 | wallabag_v1: 'Migrar dempuèi wallabag v1' |
356 | wallabag_v2: 'Migrar dempuèi wallabag v2' | 368 | wallabag_v2: 'Migrar dempuèi wallabag v2' |
@@ -358,17 +370,17 @@ quickstart: | |||
358 | instapaper: 'Migrar dempuèi Instapaper' | 370 | instapaper: 'Migrar dempuèi Instapaper' |
359 | developer: | 371 | developer: |
360 | title: 'Pels desvolopaires' | 372 | title: 'Pels desvolopaires' |
361 | description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.' | 373 | description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.' |
362 | create_application: 'Crear vòstra aplicacion tèrça' | 374 | create_application: 'Crear vòstra aplicacion tèrça' |
363 | use_docker: 'Utilizar Docker per installar wallabag' | 375 | use_docker: 'Utilizar Docker per installar wallabag' |
364 | docs: | 376 | docs: |
365 | title: 'Documentacion complèta' | 377 | title: 'Documentacion complèta' |
366 | description: "I a un fum de fonccionalitats dins wallabag. Esitetz pas a legir lo manual per las conéisser e aprendre a las utilizar." | 378 | description: "I a un fum de foncionalitats dins wallabag. Esitetz pas a legir lo manual per las conéisser e aprendre a las utilizar." |
367 | annotate: 'Anotar vòstre article' | 379 | annotate: 'Anotar vòstre article' |
368 | export: 'Convertissètz vòstres articles en ePub o en PDF' | 380 | export: 'Convertissètz vòstres articles en ePub o en PDF' |
369 | search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" | 381 | search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" |
370 | fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" | 382 | fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" |
371 | all_docs: "E encara plen de causas mai !" | 383 | all_docs: "E encara plen de causas mai !" |
372 | support: | 384 | support: |
373 | title: 'Assisténcia' | 385 | title: 'Assisténcia' |
374 | description: 'Perque avètz benlèu besonh de nos pausar una question, sèm disponibles per vosautres.' | 386 | description: 'Perque avètz benlèu besonh de nos pausar una question, sèm disponibles per vosautres.' |
@@ -380,19 +392,22 @@ tag: | |||
380 | page_title: 'Etiquetas' | 392 | page_title: 'Etiquetas' |
381 | list: | 393 | list: |
382 | number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas." | 394 | number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas." |
383 | see_untagged_entries: "Afichar las entradas sens pas cap d'etiquetas" | 395 | see_untagged_entries: "Afichar las entradas sens etiquetas" |
384 | new: | 396 | new: |
385 | add: 'Ajustar' | 397 | add: 'Ajustar' |
386 | placeholder: "Podètz ajustar mai qu'una etiqueta, separadas per de virgula." | 398 | placeholder: "Podètz ajustar mai qu'una etiqueta, separadas per de virgula." |
387 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'Importar' | 404 | page_title: 'Importar' |
390 | page_description: "Benvenguda sus l'aisina de migracion de wallabag. Causissètz çai-jos lo servici dempuèi lo qual volètz migrar." | 405 | page_description: "Benvenguda sus l'aisina de migracion de wallabag. Causissètz çai-jos lo servici dempuèi lo qual volètz migrar." |
391 | action: | 406 | action: |
392 | import_contents: 'Importar los contenguts' | 407 | import_contents: 'Importar lo contengut' |
393 | form: | 408 | form: |
394 | mark_as_read_title: 'Tot marcar coma legit ?' | 409 | mark_as_read_title: 'O marcar tot coma legit ?' |
395 | mark_as_read_label: 'Marcar tot los contenguts importats coma legits' | 410 | mark_as_read_label: 'Marcar tot lo contengut importats coma legit' |
396 | file_label: 'Fichièr' | 411 | file_label: 'Fichièr' |
397 | save_label: 'Importar lo fichièr' | 412 | save_label: 'Importar lo fichièr' |
398 | pocket: | 413 | pocket: |
@@ -406,25 +421,25 @@ import: | |||
406 | connect_to_pocket: 'Se connectar a Pocket e importar las donadas' | 421 | connect_to_pocket: 'Se connectar a Pocket e importar las donadas' |
407 | wallabag_v1: | 422 | wallabag_v1: |
408 | page_title: 'Importar > Wallabag v1' | 423 | page_title: 'Importar > Wallabag v1' |
409 | description: 'Aquesta aisina importarà totas vòstras donadas de wallabag v1. Sus vòstre pagina de configuracion de wallabag v1, clicatz sus \"Export JSON\" dins la seccion \"Exportar vòstras donadas de wallabag\". Traparatz un fichièr \"wallabag-export-1-xxxx-xx-xx.json\".' | 424 | description: 'Aquesta aisina importarà totas vòstras donadas de wallabag v1. Sus vòstre pagina de configuracion de wallabag v1, clicatz sus \"Export JSON\" dins la seccion \"Exportar vòstras donadas de wallabag\". Traparetz un fichièr \"wallabag-export-1-xxxx-xx-xx.json\".' |
410 | how_to: "Causissètz lo fichièr de vòstra exportacion wallabag v1 e clicatz sul boton çai-jos per l'importar." | 425 | how_to: "Causissètz lo fichièr de vòstra exportacion wallabag v1 e clicatz sul boton çai-jos per l'importar." |
411 | wallabag_v2: | 426 | wallabag_v2: |
412 | page_title: 'Importar > Wallabag v2' | 427 | page_title: 'Importar > Wallabag v2' |
413 | description: "Aquesta aisina importarà totas vòstras donadas d'una instància mai de wallabag v2. Anatz dins totes vòstres articles, puèi, sus la barra laterala, clicatz sus \"JSON\". Traparatz un fichièr \"All articles.json\"." | 428 | description: "Aquesta aisina importarà totas vòstras donadas d'una instància mai de wallabag v2. Anatz dins totes vòstres articles, puèi, sus la barra laterala, clicatz sus \"JSON\". Traparetz un fichièr \"All articles.json\"." |
414 | readability: | 429 | readability: |
415 | page_title: 'Importar > Readability' | 430 | page_title: 'Importar > Readability' |
416 | description: "Aquesta aisina importarà totas vòstres articles de Readability. Sus la pagina de l'aisina (https://www.readability.com/tools/), clicatz sus \"Export your data\" dins la seccion \"Data Export\". Recebretz un corrièl per telecargar un json (qu'acaba pas amb un .json de fach)." | 431 | description: "Aquesta aisina importarà totas vòstres articles de Readability. Sus la pagina de l'aisina (https://www.readability.com/tools/), clicatz sus \"Export your data\" dins la seccion \"Data Export\". Recebretz un corrièl per telecargar un json (qu'acaba pas amb un .json de fach)." |
417 | how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar." | 432 | how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar." |
418 | worker: | 433 | worker: |
419 | enabled: "L'importacion se fa de manièra asincròna. Un còp l'importacion lançada, una aisina extèrna s'ocuparà dels messatges un per un. Lo servici actual es : " | 434 | enabled: "L'importacion se fa de manièra asincròna. Un còp l'importacion lançada, una aisina extèrna s'ocuparà dels articles un per un. Lo servici actual es : " |
420 | download_images_warning: "Avètz activat lo telecargament de los imatges de vòstres articles. Combinat amb l'importacion classica, aquò pòt tardar un long moment (o benlèu fracassar). <strong>Recomandem fòrtament</strong> l'activacion de l'importacion asincròna per evitar las errors." | 435 | download_images_warning: "Avètz activat lo telecargament de los imatges de vòstres articles. Combinat amb l'importacion classica, aquò pòt tardar un long moment (o benlèu fracassar). <strong>Recomandem fòrtament</strong> l'activacion de l'importacion asincròna per evitar las errors." |
421 | firefox: | 436 | firefox: |
422 | page_title: 'Importar > Firefox' | 437 | page_title: 'Importar > Firefox' |
423 | description: "Aquesta aisina importarà totas vòstres favorits de Firefox. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." | 438 | description: "Aquesta aisina importarà totas vòstres favorits de Firefox. Anatz simplament dins vòstres marcapaginas (Ctrl+Maj+O), puèi dins \"Impòrt e salvagarda\", causissètz \"Salvagardar...\". Auretz un fichièr .json." |
424 | how_to: "Mercés de causir lo fichièr de salvagarda e de clicar sul boton dejós per l'importar. Notatz que lo tractament pòt durar un moment ja que totes los articles an d'èsser recuperats." | 439 | how_to: "Mercés de causir lo fichièr de salvagarda e de clicar sul boton dejós per l'importar. Notatz que lo tractament pòt durar un moment ja que totes los articles an d'èsser recuperats." |
425 | chrome: | 440 | chrome: |
426 | page_title: 'Importar > Chrome' | 441 | page_title: 'Importar > Chrome' |
427 | description: "Aquesta aisina importarà totas vòstres favorits de Chrome. L'emplaçament del fichièr depend de vòstre sistèma operatiu : <ul><li>Sus Linux, anatz al dorsièr <code>~/.config/chromium/Default/</code></li><li>Sus Windows, deu èsser dins <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default</code></li><li>sus OS X, deu èsser dins <code>~/Library/Application Support/Google/Chrome/Default/Bookmarks</code></li></ul>Un còp enlà, copiatz lo fichièr de favorits dins un endrech que volètz.<em><br>Notatz que s'avètz Chromium al lòc de Chrome, vos cal cambiar lo camin segon aquesta situacion.</em></p>" | 442 | description: "Aquesta aisina importarà totas vòstres favorits de Chrome. L'emplaçament del fichièr depend de vòstre sistèma operatiu : <ul><li>Sus Linux, anatz al dorsièr <code>~/.config/chromium/Default/</code></li><li>Sus Windows, deu èsser dins <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default</code></li><li>sus OS X, deu èsser dins <code>~/Library/Application Support/Google/Chrome/Default/Bookmarks</code></li></ul>Un còp enlà, copiatz lo fichièr de favorits dins un endrech que volètz.<em><br>Notatz que s'avètz Chromium al lòc de Chrome, vos cal cambiar lo camin segon aquesta situacion.</em></p>" |
428 | how_to: "Mercés de causir lo fichièr de salvagarda e de clicar sul boton dejós per l'importar. Notatz que lo tractament pòt durar un moment ja que totes los articles an d'èsser recuperats." | 443 | how_to: "Mercés de causir lo fichièr de salvagarda e de clicar sul boton dejós per l'importar. Notatz que lo tractament pòt durar un moment ja que totes los articles an d'èsser recuperats." |
429 | instapaper: | 444 | instapaper: |
430 | page_title: 'Importar > Instapaper' | 445 | page_title: 'Importar > Instapaper' |
@@ -437,7 +452,7 @@ import: | |||
437 | 452 | ||
438 | developer: | 453 | developer: |
439 | page_title: 'Gestion dels clients API' | 454 | page_title: 'Gestion dels clients API' |
440 | welcome_message: "Benvenguda sus l'API de wallabag" | 455 | welcome_message: "Vos desirem la benvenguda sus l'API de wallabag" |
441 | documentation: 'Documentacion' | 456 | documentation: 'Documentacion' |
442 | how_to_first_app: 'Cossí crear vòstra primièra aplicacion' | 457 | how_to_first_app: 'Cossí crear vòstra primièra aplicacion' |
443 | full_documentation: "Veire la documentacion completa de l'API" | 458 | full_documentation: "Veire la documentacion completa de l'API" |
@@ -448,17 +463,17 @@ developer: | |||
448 | existing_clients: | 463 | existing_clients: |
449 | title: 'Los clients existents' | 464 | title: 'Los clients existents' |
450 | field_id: 'ID Client' | 465 | field_id: 'ID Client' |
451 | field_secret: 'Clé secreta' | 466 | field_secret: 'Clau secrèta' |
452 | field_uris: 'URLs de redireccion' | 467 | field_uris: 'URLs de redireccion' |
453 | field_grant_types: 'Tipe de privilègi acordat' | 468 | field_grant_types: 'Tipe de privilègi acordat' |
454 | no_client: 'Pas cap de client pel moment.' | 469 | no_client: 'Pas cap de client pel moment.' |
455 | remove: | 470 | remove: |
456 | warn_message_1: 'Avètz la possibilitat de supriimr un client. Aquesta accion es IRREVERSIBLA !' | 471 | warn_message_1: 'Avètz la possibilitat de suprimir un client. Aquesta accion es IRREVERSIBLA !' |
457 | warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." | 472 | warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." |
458 | action: 'Suprimir aqueste client' | 473 | action: 'Suprimir aqueste client' |
459 | client: | 474 | client: |
460 | page_title: 'Gestion dels clients API > Novèl client' | 475 | 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." | 476 | page_description: "Anatz crear un novèl client. Mercés de garnir l'url de redireccion cap a vòstra aplicacion." |
462 | form: | 477 | form: |
463 | name_label: "Nom del client" | 478 | name_label: "Nom del client" |
464 | redirect_uris_label: 'URLs de redireccion' | 479 | redirect_uris_label: 'URLs de redireccion' |
@@ -469,7 +484,7 @@ developer: | |||
469 | page_description: 'Vaquí los paramètres de vòstre client.' | 484 | page_description: 'Vaquí los paramètres de vòstre client.' |
470 | field_name: 'Nom del client' | 485 | field_name: 'Nom del client' |
471 | field_id: 'ID Client' | 486 | field_id: 'ID Client' |
472 | field_secret: 'Clau secreta' | 487 | field_secret: 'Clau secrèta' |
473 | back: 'Retour' | 488 | back: 'Retour' |
474 | read_howto: 'Legir "cossí crear ma primièra aplicacion"' | 489 | read_howto: 'Legir "cossí crear ma primièra aplicacion"' |
475 | howto: | 490 | howto: |
@@ -477,10 +492,10 @@ developer: | |||
477 | description: | 492 | description: |
478 | paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar." | 493 | paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar." |
479 | paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." | 494 | paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." |
480 | paragraph_3: 'Per crear un geton, vos cal crear <a href=\"%link%\">crear un novèl client</a>.' | 495 | paragraph_3: 'Per crear un geton, vos cal <a href=\"%link%\">crear un novèl client</a>.' |
481 | paragraph_4: 'Ara creatz un geton (remplaçar client_id, client_secret, username e password amb las bonas valors) :' | 496 | paragraph_4: 'Ara creatz un geton (remplaçar client_id, client_secret, username e password amb las bonas valors) :' |
482 | paragraph_5: "L'API vos tornarà una responsa coma aquò :" | 497 | paragraph_5: "L'API vos tornarà una responsa coma aquò :" |
483 | paragraph_6: "L'access_token deu èsser emplegat per far una requèsta a l'API. Per exemple :" | 498 | paragraph_6: "L'access_token deu èsser emplegat per far una requèsta a l'API. Per exemple :" |
484 | paragraph_7: "Aquesta requèsta tornarà totes los articles de l'utilizaire." | 499 | paragraph_7: "Aquesta requèsta tornarà totes los articles de l'utilizaire." |
485 | paragraph_8: "Se volètz totas las adreças d'accès de l'API, donatz un còp d’uèlh <a href=\"%link%\">a la documentacion de l'API</a>." | 500 | paragraph_8: "Se volètz totas las adreças d'accès de l'API, donatz un còp d’uèlh <a href=\"%link%\">a la documentacion de l'API</a>." |
486 | back: 'Retorn' | 501 | back: 'Retorn' |
@@ -508,7 +523,29 @@ user: | |||
508 | twofactor_label: 'Autentificacion doble-factor' | 523 | twofactor_label: 'Autentificacion doble-factor' |
509 | save: 'Enregistrar' | 524 | save: 'Enregistrar' |
510 | delete: 'Suprimir' | 525 | delete: 'Suprimir' |
511 | delete_confirm: 'Sètz segur ?' | 526 | delete_confirm: 'Sètz segur ?' |
527 | back_to_list: 'Tornar a la lista' | ||
528 | search: | ||
529 | placeholder: "Filtrar per nom d'utilizaire o corrièl" | ||
530 | |||
531 | site_credential: | ||
532 | page_title: Gestion dels identificants | ||
533 | new_site_credential: Crear un identificant | ||
534 | edit_site_credential: Modificar un identificant | ||
535 | description: "Aquí podètz gerir vòstres identificants pels sites que los demandan (ne crear, ne modifiar, ne suprimir) coma los sites a peatge, etc." | ||
536 | list: | ||
537 | actions: 'Accions' | ||
538 | edit_action: 'Modificar' | ||
539 | yes: 'Òc' | ||
540 | no: 'Non' | ||
541 | create_new_one: Crear un novèl identificant | ||
542 | form: | ||
543 | username_label: "Nom d'utilizaire" | ||
544 | host_label: 'Òste' | ||
545 | password_label: 'Senhal' | ||
546 | save: 'Enregistrar' | ||
547 | delete: 'Suprimir' | ||
548 | delete_confirm: 'Sètz segur ?' | ||
512 | back_to_list: 'Tornar a la lista' | 549 | back_to_list: 'Tornar a la lista' |
513 | 550 | ||
514 | error: | 551 | error: |
@@ -519,7 +556,7 @@ flashes: | |||
519 | notice: | 556 | notice: |
520 | config_saved: 'Los paramètres son ben estats meses a jorn.' | 557 | config_saved: 'Los paramètres son ben estats meses a jorn.' |
521 | password_updated: 'Vòstre senhal es ben estat mes a jorn' | 558 | password_updated: 'Vòstre senhal es ben estat mes a jorn' |
522 | password_not_updated_demo: "En demostration, podètz pas cambiar lo senhal d'aqueste utilizaire." | 559 | password_not_updated_demo: "En demostracion, podètz pas cambiar lo senhal d'aqueste utilizaire." |
523 | user_updated: 'Vòstres informacions personnelas son ben estadas mesas a jorn' | 560 | user_updated: 'Vòstres informacions personnelas son ben estadas mesas a jorn' |
524 | rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' | 561 | rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' |
525 | tagging_rules_updated: 'Règlas misa a jorn' | 562 | tagging_rules_updated: 'Règlas misa a jorn' |
@@ -528,9 +565,10 @@ flashes: | |||
528 | annotations_reset: Anotacions levadas | 565 | annotations_reset: Anotacions levadas |
529 | tags_reset: Etiquetas levadas | 566 | tags_reset: Etiquetas levadas |
530 | entries_reset: Articles levats | 567 | entries_reset: Articles levats |
568 | archived_reset: Articles archivat suprimits | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'Article ja salvargardat lo %date%' | 571 | entry_already_saved: 'Article ja salvagardat lo %date%' |
534 | entry_saved: 'Article enregistrat' | 572 | entry_saved: 'Article enregistrat' |
535 | entry_saved_failed: 'Article salvat mai fracàs de la recuperacion del contengut' | 573 | entry_saved_failed: 'Article salvat mai fracàs de la recuperacion del contengut' |
536 | entry_updated: 'Article mes a jorn' | 574 | entry_updated: 'Article mes a jorn' |
@@ -547,9 +585,9 @@ flashes: | |||
547 | import: | 585 | import: |
548 | notice: | 586 | notice: |
549 | failed: "L'importacion a fracassat, mercés de tornar ensajar." | 587 | failed: "L'importacion a fracassat, mercés de tornar ensajar." |
550 | failed_on_file: "Errorr pendent du tractament de l'import. Mercés de verificar vòstre fichièr." | 588 | failed_on_file: "Error en tractar l'impòrt. Mercés de verificar vòstre fichièr." |
551 | summary: "Rapòrt d'import: %imported% importats, %skipped% ja presents." | 589 | summary: "Rapòrt d'impòrt: %imported% importats, %skipped% ja presents." |
552 | summary_with_queue: "Rapòrt d'import : %queued% en espèra de tractament." | 590 | summary_with_queue: "Rapòrt d'impòrt : %queued% en espèra de tractament." |
553 | error: | 591 | error: |
554 | redis_enabled_not_installed: "Redis es capable d'importar de manièra asincròna mai sembla que <u>podèm pas nos conectar amb el</u>. Mercés de verificar la configuracion de Redis." | 592 | redis_enabled_not_installed: "Redis es capable d'importar de manièra asincròna mai sembla que <u>podèm pas nos conectar amb el</u>. Mercés de verificar la configuracion de Redis." |
555 | rabbit_enabled_not_installed: "RabbitMQ es capable d'importar de manièra asincròna mai sembla que <u>podèm pas nos conectar amb el</u>. Mercés de verificar la configuracion de RabbitMQ." | 593 | rabbit_enabled_not_installed: "RabbitMQ es capable d'importar de manièra asincròna mai sembla que <u>podèm pas nos conectar amb el</u>. Mercés de verificar la configuracion de RabbitMQ." |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'Utilizaire "%username%" ajustat' | 600 | added: 'Utilizaire "%username%" ajustat' |
563 | updated: 'Utilizaire "%username%" mes a jorn' | 601 | updated: 'Utilizaire "%username%" mes a jorn' |
564 | deleted: 'Utilizaire "%username%" suprimit' | 602 | deleted: 'Utilizaire "%username%" suprimit' |
603 | site_credential: | ||
604 | notice: | ||
605 | added: 'Identificant per "%host%" ajustat' | ||
606 | updated: 'Identificant per "%host%" mes a jorn' | ||
607 | deleted: 'Identificant per "%host%" suprimit' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index b990a6b9..e642c530 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: 'Poświadczenia strony' | ||
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: 'Wszystkie' | ||
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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | filtered_tags: 'Filtrowane po tagach:' | 169 | filtered_tags: 'Filtrowane po tagach:' |
165 | filtered_search: 'Filtrowanie po wyszukiwaniu:' | 170 | filtered_search: 'Filtrowanie po wyszukiwaniu:' |
166 | untagged: 'Odtaguj wpisy' | 171 | untagged: 'Odtaguj wpisy' |
172 | all: 'Wszystkie przedmioty' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.' | 174 | number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.' |
169 | reading_time: 'szacunkowy czas czytania' | 175 | reading_time: 'szacunkowy czas czytania' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Nieprzeczytane' | 191 | unread_label: 'Nieprzeczytane' |
186 | preview_picture_label: 'Posiada podgląd obrazu' | 192 | preview_picture_label: 'Posiada podgląd obrazu' |
187 | preview_picture_help: 'Podgląd obrazu' | 193 | preview_picture_help: 'Podgląd obrazu' |
194 | is_public_label: 'Posiada publiczny link' | ||
195 | is_public_help: 'Publiczny link' | ||
188 | language_label: 'Język' | 196 | language_label: 'Język' |
189 | http_status_label: 'Status HTTP' | 197 | http_status_label: 'Status HTTP' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'oryginalny' | 231 | original_article: 'oryginalny' |
224 | annotations_on_the_entry: '{0} Nie ma adnotacji |{1} Jedna adnotacja |]1,Inf[ %count% adnotacji' | 232 | annotations_on_the_entry: '{0} Nie ma adnotacji |{1} Jedna adnotacja |]1,Inf[ %count% adnotacji' |
225 | created_at: 'Czas stworzenia' | 233 | created_at: 'Czas stworzenia' |
234 | published_at: 'Data publikacji' | ||
235 | published_by: 'Opublikowane przez' | ||
226 | new: | 236 | new: |
227 | page_title: 'Zapisz nowy wpis' | 237 | page_title: 'Zapisz nowy wpis' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: 'Edytuj wpis' | 244 | page_title: 'Edytuj wpis' |
235 | title_label: 'Tytuł' | 245 | title_label: 'Tytuł' |
236 | url_label: 'Adres URL' | 246 | url_label: 'Adres URL' |
237 | is_public_label: 'Publiczny' | ||
238 | save_label: 'Zapisz' | 247 | save_label: 'Zapisz' |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "Ten artykuł został udostępniony przez <a href='%wallabag_instance%'>wallabag</a>" | 249 | shared_by_wallabag: "Ten artykuł został udostępniony przez <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?" | ||
252 | delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'O nas' | 255 | page_title: 'O nas' |
@@ -385,6 +397,9 @@ tag: | |||
385 | add: 'Dodaj' | 397 | add: 'Dodaj' |
386 | placeholder: 'Możesz dodać kilka tagów, oddzielając je przecinkami.' | 398 | placeholder: 'Możesz dodać kilka tagów, oddzielając je przecinkami.' |
387 | 399 | ||
400 | export: | ||
401 | footer_template: '<div style="text-align:center;"><p>Stworzone przez wallabag z %method%</p><p>Proszę zgłoś <a href="https://github.com/wallabag/wallabag/issues">sprawę</a>, jeżeli masz problem z wyświetleniem tego e-booka na swoim urządzeniu.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'Import' | 404 | page_title: 'Import' |
390 | page_description: 'Witaj w importerze Wallabag. Wybierz swoją poprzednią usługę, z której chcesz migrować.' | 405 | page_description: 'Witaj w importerze Wallabag. Wybierz swoją poprzednią usługę, z której chcesz migrować.' |
@@ -510,6 +525,28 @@ user: | |||
510 | delete: Usuń | 525 | delete: Usuń |
511 | delete_confirm: Jesteś pewien? | 526 | delete_confirm: Jesteś pewien? |
512 | back_to_list: Powrót do listy | 527 | back_to_list: Powrót do listy |
528 | search: | ||
529 | placeholder: Filtruj po nazwie użytkownika lub adresie e-mail | ||
530 | |||
531 | site_credential: | ||
532 | page_title: Zarządzanie poświadczeniami strony | ||
533 | new_site_credential: Stwórz nowe poświadczenie | ||
534 | edit_site_credential: Edytuj istniejące poświadczenie | ||
535 | description: "Tutaj możesz zarządzać wszystkim poświadczeniami wymaganymi przez strony (stwórz, edytuj i usuń ), takie jak paywall, autentykacja, itp." | ||
536 | list: | ||
537 | actions: Akcje | ||
538 | edit_action: Edytuj | ||
539 | yes: Tak | ||
540 | no: Nie | ||
541 | create_new_one: Stwórz nowe poświadczenie | ||
542 | form: | ||
543 | username_label: 'Nazwa użytkownika' | ||
544 | host_label: 'Host' | ||
545 | password_label: 'Hasło' | ||
546 | save: Zapisz | ||
547 | delete: Usuń | ||
548 | delete_confirm: Jesteś pewien? | ||
549 | back_to_list: Powrót do listy | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | page_title: Wystąpił błąd | 552 | page_title: Wystąpił błąd |
@@ -528,6 +565,7 @@ flashes: | |||
528 | annotations_reset: Zresetuj adnotacje | 565 | annotations_reset: Zresetuj adnotacje |
529 | tags_reset: Zresetuj tagi | 566 | tags_reset: Zresetuj tagi |
530 | entries_reset: Zresetuj wpisy | 567 | entries_reset: Zresetuj wpisy |
568 | archived_reset: Zarchiwizowane wpisy usunięte | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'Wpis już został dodany %date%' | 571 | entry_already_saved: 'Wpis już został dodany %date%' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'Użytkownik "%username%" dodany' | 600 | added: 'Użytkownik "%username%" dodany' |
563 | updated: 'Użytkownik "%username%" zaktualizowany' | 601 | updated: 'Użytkownik "%username%" zaktualizowany' |
564 | deleted: 'Użytkownik "%username%" usunięty' | 602 | deleted: 'Użytkownik "%username%" usunięty' |
603 | site_credential: | ||
604 | notice: | ||
605 | added: 'Poświadczenie dla "%host%" dodane' | ||
606 | updated: 'Poświadczenie dla "%host%" zaktualizowane' | ||
607 | deleted: 'Poświadczenie dla "%host%" usuniętę' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml index 3b1f9cb6..9b3fea6b 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | filtered_tags: 'Filtrar por tags:' | 169 | filtered_tags: 'Filtrar por tags:' |
165 | # filtered_search: 'Filtered by search:' | 170 | # filtered_search: 'Filtered by search:' |
166 | untagged: 'Entradas sem tags' | 171 | untagged: 'Entradas sem tags' |
172 | # all: 'All entries' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.' | 174 | number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.' |
169 | reading_time: 'tempo estimado de leitura' | 175 | reading_time: 'tempo estimado de leitura' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Não Lido' | 191 | unread_label: 'Não Lido' |
186 | preview_picture_label: 'Possui uma imagem de preview' | 192 | preview_picture_label: 'Possui uma imagem de preview' |
187 | preview_picture_help: 'Imagem de preview' | 193 | preview_picture_help: 'Imagem de preview' |
194 | # is_public_label: 'Has a public link' | ||
195 | # is_public_help: 'Public link' | ||
188 | language_label: 'Idioma' | 196 | language_label: 'Idioma' |
189 | # http_status_label: 'HTTP status' | 197 | # http_status_label: 'HTTP status' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'original' | 231 | original_article: 'original' |
224 | annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações' | 232 | annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações' |
225 | created_at: 'Data de criação' | 233 | created_at: 'Data de criação' |
234 | # published_at: 'Publication date' | ||
235 | # published_by: 'Published by' | ||
226 | new: | 236 | new: |
227 | page_title: 'Salvar nova entrada' | 237 | page_title: 'Salvar nova entrada' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | page_title: 'Editar uma entrada' | 244 | page_title: 'Editar uma entrada' |
235 | title_label: 'Título' | 245 | title_label: 'Título' |
236 | url_label: 'Url' | 246 | url_label: 'Url' |
237 | is_public_label: 'Público' | ||
238 | save_label: 'Salvar' | 247 | save_label: 'Salvar' |
239 | public: | 248 | public: |
240 | shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>" | 249 | shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | # delete: "Are you sure you want to remove that article?" | ||
252 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'Sobre' | 255 | page_title: 'Sobre' |
@@ -385,6 +397,9 @@ tag: | |||
385 | # add: 'Add' | 397 | # add: 'Add' |
386 | # placeholder: 'You can add several tags, separated by a comma.' | 398 | # placeholder: 'You can add several tags, separated by a comma.' |
387 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | page_title: 'Importar' | 404 | page_title: 'Importar' |
390 | page_description: 'Bem-vindo ao importador do wallabag. Por favo selecione o serviço do qual deseja migrar.' | 405 | page_description: 'Bem-vindo ao importador do wallabag. Por favo selecione o serviço do qual deseja migrar.' |
@@ -510,6 +525,28 @@ user: | |||
510 | delete: 'Apagar' | 525 | delete: 'Apagar' |
511 | delete_confirm: 'Tem certeza?' | 526 | delete_confirm: 'Tem certeza?' |
512 | back_to_list: 'Voltar para a lista' | 527 | back_to_list: 'Voltar para a lista' |
528 | search: | ||
529 | # placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | # page_title: Site credentials management | ||
533 | # new_site_credential: Create a credential | ||
534 | # edit_site_credential: Edit an existing credential | ||
535 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | list: | ||
537 | actions: 'Ações' | ||
538 | edit_action: 'Editar' | ||
539 | yes: 'Sim' | ||
540 | no: 'Não' | ||
541 | # create_new_one: Create a new credential | ||
542 | form: | ||
543 | # username_label: 'Username' | ||
544 | # host_label: 'Host' | ||
545 | # password_label: 'Password' | ||
546 | save: 'Salvar' | ||
547 | delete: 'Apagar' | ||
548 | delete_confirm: 'Tem certeza?' | ||
549 | back_to_list: 'Voltar para a lista' | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | # page_title: An error occurred | 552 | # page_title: An error occurred |
@@ -528,6 +565,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 565 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 566 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 567 | # entries_reset: Entries reset |
568 | # archived_reset: Archived entries deleted | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | entry_already_saved: 'Entrada já foi salva em %date%' | 571 | entry_already_saved: 'Entrada já foi salva em %date%' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | added: 'Usuário "%username%" adicionado' | 600 | added: 'Usuário "%username%" adicionado' |
563 | updated: 'Usuário "%username%" atualizado' | 601 | updated: 'Usuário "%username%" atualizado' |
564 | deleted: 'Usuário "%username%" removido' | 602 | deleted: 'Usuário "%username%" removido' |
603 | site_credential: | ||
604 | notice: | ||
605 | # added: 'Site credential for "%host%" added' | ||
606 | # updated: 'Site credential for "%host%" updated' | ||
607 | # 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..673ca183 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | # filtered_tags: 'Filtered by tags:' | 169 | # filtered_tags: 'Filtered by tags:' |
165 | # filtered_search: 'Filtered by search:' | 170 | # filtered_search: 'Filtered by search:' |
166 | # untagged: 'Untagged entries' | 171 | # untagged: 'Untagged entries' |
172 | # all: 'All entries' | ||
167 | list: | 173 | list: |
168 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' | 174 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' |
169 | reading_time: 'timp estimat de citire' | 175 | reading_time: 'timp estimat de citire' |
@@ -185,6 +191,8 @@ entry: | |||
185 | unread_label: 'Necitite' | 191 | unread_label: 'Necitite' |
186 | preview_picture_label: 'Are o imagine de previzualizare' | 192 | preview_picture_label: 'Are o imagine de previzualizare' |
187 | preview_picture_help: 'Previzualizare imagine' | 193 | preview_picture_help: 'Previzualizare imagine' |
194 | # is_public_label: 'Has a public link' | ||
195 | # is_public_help: 'Public link' | ||
188 | language_label: 'Limbă' | 196 | language_label: 'Limbă' |
189 | # http_status_label: 'HTTP status' | 197 | # http_status_label: 'HTTP status' |
190 | reading_time: | 198 | reading_time: |
@@ -223,6 +231,8 @@ entry: | |||
223 | original_article: 'original' | 231 | original_article: 'original' |
224 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 232 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Data creării' | 233 | created_at: 'Data creării' |
234 | # published_at: 'Publication date' | ||
235 | # published_by: 'Published by' | ||
226 | new: | 236 | new: |
227 | page_title: 'Salvează un nou articol' | 237 | page_title: 'Salvează un nou articol' |
228 | placeholder: 'http://website.com' | 238 | placeholder: 'http://website.com' |
@@ -234,10 +244,12 @@ entry: | |||
234 | # page_title: 'Edit an entry' | 244 | # page_title: 'Edit an entry' |
235 | # title_label: 'Title' | 245 | # title_label: 'Title' |
236 | url_label: 'Url' | 246 | url_label: 'Url' |
237 | # is_public_label: 'Public' | ||
238 | save_label: 'Salvează' | 247 | save_label: 'Salvează' |
239 | public: | 248 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 249 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
250 | confirm: | ||
251 | # delete: "Are you sure you want to remove that article?" | ||
252 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 253 | ||
242 | about: | 254 | about: |
243 | page_title: 'Despre' | 255 | page_title: 'Despre' |
@@ -385,6 +397,9 @@ tag: | |||
385 | # add: 'Add' | 397 | # add: 'Add' |
386 | # placeholder: 'You can add several tags, separated by a comma.' | 398 | # placeholder: 'You can add several tags, separated by a comma.' |
387 | 399 | ||
400 | # export: | ||
401 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
402 | |||
388 | import: | 403 | import: |
389 | # page_title: 'Import' | 404 | # page_title: 'Import' |
390 | # page_description: 'Welcome to wallabag importer. Please select your previous service that you want to migrate.' | 405 | # page_description: 'Welcome to wallabag importer. Please select your previous service that you want to migrate.' |
@@ -510,6 +525,28 @@ user: | |||
510 | # delete: Delete | 525 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 526 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 527 | # back_to_list: Back to list |
528 | search: | ||
529 | # placeholder: Filter by username or email | ||
530 | |||
531 | site_credential: | ||
532 | # page_title: Site credentials management | ||
533 | # new_site_credential: Create a credential | ||
534 | # edit_site_credential: Edit an existing credential | ||
535 | # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." | ||
536 | # list: | ||
537 | # actions: Actions | ||
538 | # edit_action: Edit | ||
539 | # yes: Yes | ||
540 | # no: No | ||
541 | # create_new_one: Create a new credential | ||
542 | # form: | ||
543 | # username_label: 'Username' | ||
544 | # host_label: 'Host' | ||
545 | # password_label: 'Password' | ||
546 | # save: Save | ||
547 | # delete: Delete | ||
548 | # delete_confirm: Are you sure? | ||
549 | # back_to_list: Back to list | ||
513 | 550 | ||
514 | error: | 551 | error: |
515 | # page_title: An error occurred | 552 | # page_title: An error occurred |
@@ -528,6 +565,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 565 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 566 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 567 | # entries_reset: Entries reset |
568 | # archived_reset: Archived entries deleted | ||
531 | entry: | 569 | entry: |
532 | notice: | 570 | notice: |
533 | # entry_already_saved: 'Entry already saved on %date%' | 571 | # entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +600,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 600 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 601 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 602 | # deleted: 'User "%username%" deleted' |
603 | site_credential: | ||
604 | notice: | ||
605 | # added: 'Site credential for "%host%" added' | ||
606 | # updated: 'Site credential for "%host%" updated' | ||
607 | # deleted: 'Site credential for "%host%" deleted' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml new file mode 100644 index 00000000..eceecabf --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml | |||
@@ -0,0 +1,564 @@ | |||
1 | security: | ||
2 | login: | ||
3 | page_title: 'Приветствую в wallabag!' | ||
4 | keep_logged_in: 'Запомнить меня' | ||
5 | forgot_password: 'Забыли пароль?' | ||
6 | submit: 'Войти' | ||
7 | register: 'Регистрация' | ||
8 | username: 'Логин' | ||
9 | password: 'Пароль' | ||
10 | cancel: 'Отменить' | ||
11 | resetting: | ||
12 | description: "Введите Ваш email ниже и мы отправим Вам инструкцию для сброса пароля." | ||
13 | register: | ||
14 | page_title: 'Создать новый аккаунт' | ||
15 | go_to_account: 'Перейти в аккаунт' | ||
16 | |||
17 | menu: | ||
18 | left: | ||
19 | unread: 'Непрочитанные' | ||
20 | starred: 'Помеченные' | ||
21 | archive: 'Архивные' | ||
22 | all_articles: 'Все записи' | ||
23 | config: 'Настройки' | ||
24 | tags: 'Теги' | ||
25 | internal_settings: 'Внутренние настройки' | ||
26 | import: 'Импорт' | ||
27 | howto: 'How to' | ||
28 | developer: 'Настройки клиентского API' | ||
29 | logout: 'Выход' | ||
30 | about: 'о wallabag' | ||
31 | search: 'Поиск' | ||
32 | save_link: 'Сохранить ссылку' | ||
33 | back_to_unread: 'Назад к непрочитанным записям' | ||
34 | users_management: 'Управление пользователями' | ||
35 | top: | ||
36 | add_new_entry: 'Добавить новую запись' | ||
37 | search: 'Поиск' | ||
38 | filter_entries: 'Фильтр записей' | ||
39 | export: 'Экспорт' | ||
40 | search_form: | ||
41 | input_label: 'Введите текст для поиска' | ||
42 | |||
43 | footer: | ||
44 | wallabag: | ||
45 | elsewhere: 'Возьмите wallabag с собой' | ||
46 | social: 'Соц. сети' | ||
47 | powered_by: 'создано с помощью' | ||
48 | about: 'О wallabag' | ||
49 | stats: "Поскольку %user_creation% вы читаете %nb_archives% записей, это около %per_day% в день!" | ||
50 | |||
51 | config: | ||
52 | page_title: 'Настройки' | ||
53 | tab_menu: | ||
54 | settings: 'Настройки' | ||
55 | rss: 'RSS' | ||
56 | user_info: 'Информация о пользователе' | ||
57 | password: 'Пароль' | ||
58 | rules: 'Правила настройки простановки тегов' | ||
59 | new_user: 'Добавить пользователя' | ||
60 | form: | ||
61 | save: 'Сохранить' | ||
62 | form_settings: | ||
63 | theme_label: 'Тема' | ||
64 | items_per_page_label: 'Записей на странице' | ||
65 | language_label: 'Язык' | ||
66 | reading_speed: | ||
67 | label: 'Скорость чтения' | ||
68 | help_message: 'Вы можете использовать онлайн-инструменты для оценки скорости чтения:' | ||
69 | 100_word: 'Я читаю ~100 слов в минуту' | ||
70 | 200_word: 'Я читаю ~200 слов в минуту' | ||
71 | 300_word: 'Я читаю ~300 слов в минуту' | ||
72 | 400_word: 'Я читаю ~400 слов в минуту' | ||
73 | action_mark_as_read: | ||
74 | label: 'Куда Вы хотите быть перенаправлены, после пометки записи, как прочитанная?' | ||
75 | redirect_homepage: 'На домашнюю страницу' | ||
76 | redirect_current_page: 'На текущую страницу' | ||
77 | pocket_consumer_key_label: "Ключ от Pocket для импорта контента" | ||
78 | android_configuration: "Настройте Ваше Android приложение" | ||
79 | help_theme: "wallabag настраиваемый, здесь Вы можете выбрать тему." | ||
80 | help_items_per_page: "Вы можете выбрать количество отображаемых записей на странице." | ||
81 | help_reading_speed: "wallabag посчитает сколько времени занимает чтение каждой записи. Вы можете определить здесь, как быстро вы читаете. wallabag пересчитает время чтения для каждой записи." | ||
82 | help_language: "Вы можете изменить язык интерфейса wallabag." | ||
83 | help_pocket_consumer_key: "Обязательно для импорта из Pocket. Вы можете создать это в Вашем аккаунте на Pocket." | ||
84 | form_rss: | ||
85 | description: 'RSS фид созданный с помощью wallabag позволяет читать Ваши записи через Ваш любимый RSS агрегатор. Для начала Вам потребуется создать ключ.' | ||
86 | token_label: 'RSS ключ' | ||
87 | no_token: 'Ключ не задан' | ||
88 | token_create: 'Создать ключ' | ||
89 | token_reset: 'Пересоздать ключ' | ||
90 | rss_links: 'ссылка на RSS' | ||
91 | rss_link: | ||
92 | unread: 'непрочитанные' | ||
93 | starred: 'помеченные' | ||
94 | archive: 'архивные' | ||
95 | rss_limit: 'Количество записей в фиде' | ||
96 | form_user: | ||
97 | two_factor_description: "Включить двухфакторную аутентификацию, Вы получите сообщение на указанный email с кодом, при каждом новом непроверенном подключении." | ||
98 | name_label: 'Имя' | ||
99 | email_label: 'Email' | ||
100 | twoFactorAuthentication_label: 'Двухфакторная аутентификация' | ||
101 | help_twoFactorAuthentication: "Если Вы включите двухфакторную аутентификацию, то Вы будете получать код на указанный ранее email, каждый раз при входе в wallabag." | ||
102 | delete: | ||
103 | title: "Удалить мой аккаунт (или опасная зона)" | ||
104 | description: "Если Вы удалите ваш аккаунт, ВСЕ ваши записи, теги и другие данные, будут БЕЗВОЗВРАТНО удалены (операция не может быть отменена после). Затем Вы выйдете из системы." | ||
105 | confirm: "Вы точно уверены? (Данные будут БЕЗВОЗВРАТНО удалены, эти действия необратимы)" | ||
106 | button: "Удалить мой аккаунт" | ||
107 | reset: | ||
108 | title: "Сброс данных (или опасная зона)" | ||
109 | description: "По нажатию на кнопки ниже, Вы сможете удалить часть данных из Вашего аккаунта. Будьте осторожны, эти действия необратимы." | ||
110 | annotations: "Удалить все аннотации" | ||
111 | tags: "Удалить все теги" | ||
112 | entries: "Удалить все записи" | ||
113 | confirm: "Вы уверены? (Данные будут БЕЗВОЗВРАТНО удалены, эти действия необратимы)" | ||
114 | form_password: | ||
115 | description: "Здесь Вы можете поменять своя пароль. Ваш пароль должен быть длиннее 8 символов." | ||
116 | old_password_label: 'Текущий пароль' | ||
117 | new_password_label: 'Новый пароль' | ||
118 | repeat_new_password_label: 'Повторите новый пароль' | ||
119 | form_rules: | ||
120 | if_label: 'Если' | ||
121 | then_tag_as_label: 'тогда тег' | ||
122 | delete_rule_label: 'удалить' | ||
123 | edit_rule_label: 'изменить' | ||
124 | rule_label: 'Правило' | ||
125 | tags_label: 'теги' | ||
126 | faq: | ||
127 | title: 'FAQ' | ||
128 | tagging_rules_definition_title: 'Что значит "правило тегирования"?' | ||
129 | tagging_rules_definition_description: 'Правила, по которым Wallabag автоматически добавит теги для новых записей.<br />Каждый раз, при добавлении новых записей, будут проставляться теги к записям, согласно настроенным правилам тегирования, это избавит Вас от необходимости проставлять теги для каждой записи вручную.' | ||
130 | how_to_use_them_title: 'Как мне их использовать?' | ||
131 | how_to_use_them_description: 'Предположим, вы хотите пометить новые записи как "<i>короткая</i>", когда на чтение уйдет меньше 3 минут.<br />В этом случае, установите " readingTime <= 3 " в поле <i>Правила</i> и "<i>короткая</i>" в поле <i>Теги</i>.<br />Несколько тегов могут добавляться одновременно, разделяя их запятой: "<i>короткая, прочитать обязательно</i>" <br />Сложные правила могут быть записаны с использованием предопределенных операторов: если "<i>readingTime >= 5 AND domainName = \"github.com\"</i> " тогда тег будет "<i>долго читать, github </i>"' | ||
132 | variables_available_title: 'Какие переменные и операторы я могу использовать для написания правил?' | ||
133 | variables_available_description: 'Следующие переменные и операторы могут использоваться для создания правил тегов:' | ||
134 | meaning: 'Смысл' | ||
135 | variable_description: | ||
136 | label: 'Переменные' | ||
137 | title: 'Название записи' | ||
138 | url: 'Ссылка на запись' | ||
139 | isArchived: 'Независимо от того, архивная запись или нет' | ||
140 | isStarred: 'Независимо от того, помечена запись или нет' | ||
141 | content: "Содержимое записи" | ||
142 | language: "Язык записи" | ||
143 | mimetype: "Mime-type записи" | ||
144 | readingTime: "Оценочное время чтения записи в минутах" | ||
145 | domainName: 'Домен, с которого была скачана запись' | ||
146 | operator_description: | ||
147 | label: 'Оператор' | ||
148 | less_than: 'Меньше чем...' | ||
149 | strictly_less_than: 'Строго меньше чем...' | ||
150 | greater_than: 'Больше чем...' | ||
151 | strictly_greater_than: 'Строго больше чем...' | ||
152 | equal_to: 'Равно...' | ||
153 | not_equal_to: 'Не равно...' | ||
154 | or: 'Одно правило ИЛИ другое' | ||
155 | and: 'Одно правило И другое' | ||
156 | matches: 'Тесты, в которых <i> тема </i> соответствует <i> поиску </i> (без учета регистра). Пример: <code> title matches "футбол" </code>' | ||
157 | |||
158 | entry: | ||
159 | page_titles: | ||
160 | unread: 'Непрочитанные записи' | ||
161 | starred: 'Помеченные записи' | ||
162 | archived: 'Архивные записи' | ||
163 | filtered: 'Отфильтрованные записи' | ||
164 | filtered_tags: 'Отфильтрованные по тегу:' | ||
165 | filtered_search: 'Отфильтрованные по поиску:' | ||
166 | untagged: 'Записи без тегов' | ||
167 | list: | ||
168 | number_on_the_page: '{0} Записей не обнаружено.|{1} Одна запись.|]1,Inf[ Найдено %count% записей.' | ||
169 | reading_time: 'расчетное время чтения' | ||
170 | reading_time_minutes: 'расчетное время чтения: %readingTime% мин' | ||
171 | reading_time_less_one_minute: 'расчетное время чтения: < 1 мин' | ||
172 | number_of_tags: '{1}и один другой тег|]1,Inf[и %count% другие теги' | ||
173 | reading_time_minutes_short: '%readingTime% мин' | ||
174 | reading_time_less_one_minute_short: '< 1 мин' | ||
175 | original_article: 'оригинал' | ||
176 | toogle_as_read: 'Пометить, как прочитанное' | ||
177 | toogle_as_star: 'Пометить звездочкой' | ||
178 | delete: 'Удалить' | ||
179 | export_title: 'Экспорт' | ||
180 | filters: | ||
181 | title: 'Фильтры' | ||
182 | status_label: 'Статус' | ||
183 | archived_label: 'Архивная' | ||
184 | starred_label: 'Помеченная звездочкой' | ||
185 | unread_label: 'Непрочитанная' | ||
186 | preview_picture_label: 'Есть картинка предварительного просмотра' | ||
187 | preview_picture_help: 'Картинка предварительного просмотра' | ||
188 | language_label: 'Язык' | ||
189 | http_status_label: 'статус HTTP' | ||
190 | reading_time: | ||
191 | label: 'Время чтения в минутах' | ||
192 | from: 'с' | ||
193 | to: 'по' | ||
194 | domain_label: 'Доменное имя' | ||
195 | created_at: | ||
196 | label: 'Дата создания' | ||
197 | from: 'с' | ||
198 | to: 'по' | ||
199 | action: | ||
200 | clear: 'Очистить' | ||
201 | filter: 'Выбрать' | ||
202 | view: | ||
203 | left_menu: | ||
204 | back_to_top: 'Наверх' | ||
205 | back_to_homepage: 'Назад' | ||
206 | set_as_read: 'Пометить, как прочитанное' | ||
207 | set_as_unread: 'Пометить, как не прочитанное' | ||
208 | set_as_starred: 'Пометить звездочкой' | ||
209 | view_original_article: 'Оригинальное название' | ||
210 | re_fetch_content: 'Перезагрузить содержимое' | ||
211 | delete: 'Удалить' | ||
212 | add_a_tag: 'Добавить тег' | ||
213 | share_content: 'Поделиться' | ||
214 | share_email_label: 'Email' | ||
215 | public_link: 'публичная ссылка' | ||
216 | delete_public_link: 'удалить публичную ссылку' | ||
217 | export: 'Экспорт' | ||
218 | print: 'Печатать' | ||
219 | problem: | ||
220 | label: 'Проблемы?' | ||
221 | description: 'Что-то не так с записью?' | ||
222 | edit_title: 'Изменить название' | ||
223 | original_article: 'оригинал' | ||
224 | annotations_on_the_entry: '{0} Нет аннотации|{1} Одна аннотация|]1,Inf[ %count% аннотаций' | ||
225 | created_at: 'Дата создания' | ||
226 | new: | ||
227 | page_title: 'Сохранить новую запись' | ||
228 | placeholder: 'http://website.com' | ||
229 | form_new: | ||
230 | url_label: Ссылка | ||
231 | search: | ||
232 | placeholder: 'Что Вы ищете?' | ||
233 | edit: | ||
234 | page_title: 'Изменить запись' | ||
235 | title_label: 'Название' | ||
236 | url_label: 'Ссылка' | ||
237 | is_public_label: 'Публичная' | ||
238 | save_label: 'Сохранить' | ||
239 | public: | ||
240 | shared_by_wallabag: "Запись была опубликована <a href='%wallabag_instance%'>wallabag</a>" | ||
241 | |||
242 | about: | ||
243 | page_title: 'О' | ||
244 | top_menu: | ||
245 | who_behind_wallabag: 'Кто стоит за wallabag' | ||
246 | getting_help: 'Помощь' | ||
247 | helping: 'Помочь wallabag' | ||
248 | contributors: 'Авторы' | ||
249 | third_party: 'Сторонние библиотеки' | ||
250 | who_behind_wallabag: | ||
251 | developped_by: 'Разработано' | ||
252 | website: 'веб-сайт' | ||
253 | many_contributors: 'и другие разработчики ♥ <a href="https://github.com/wallabag/wallabag/graphs/contributors">на Github</a>' | ||
254 | project_website: 'Веб-сайт проекта' | ||
255 | license: 'Лицензия' | ||
256 | version: 'Версия' | ||
257 | getting_help: | ||
258 | documentation: 'Документация' | ||
259 | bug_reports: 'Отчеты об ошибках' | ||
260 | support: '<a href="https://github.com/wallabag/wallabag/issues">на GitHub</a>' | ||
261 | helping: | ||
262 | description: 'wallabag является бесплатным и открытым исходным кодом. Вы можете помочь нам:' | ||
263 | by_contributing: 'путем внесения вклада в проект:' | ||
264 | by_contributing_2: 'с решением наших проблем' | ||
265 | by_paypal: 'с помощью Paypal' | ||
266 | contributors: | ||
267 | description: 'Спасибо вам за вклад в веб-приложение wallabag' | ||
268 | third_party: | ||
269 | description: 'Ниже приведен список сторонних библиотек, используемых в wallabag (с их лицензиями):' | ||
270 | package: 'Пакет' | ||
271 | license: 'Лицензия' | ||
272 | |||
273 | howto: | ||
274 | page_title: 'How to' | ||
275 | tab_menu: | ||
276 | add_link: "Добавить ссылку" | ||
277 | shortcuts: "Использовать горячии клавиши" | ||
278 | page_description: 'Несколько способов сохранить запись:' | ||
279 | top_menu: | ||
280 | browser_addons: 'Посмотреть дополнения' | ||
281 | mobile_apps: 'Мобильные приложения' | ||
282 | bookmarklet: 'Закладка' | ||
283 | form: | ||
284 | description: 'Спасибо за эту форму' | ||
285 | browser_addons: | ||
286 | firefox: 'Аддоны для Firefox' | ||
287 | chrome: 'Аддоны для Chrome' | ||
288 | opera: 'Аддоны для Opera' | ||
289 | mobile_apps: | ||
290 | android: | ||
291 | via_f_droid: 'в F-Droid' | ||
292 | via_google_play: 'в Google Play' | ||
293 | ios: 'в iTunes Store' | ||
294 | windows: 'в Microsoft Store' | ||
295 | bookmarklet: | ||
296 | description: 'Drag & drop эту ссылку в ваши закладки:' | ||
297 | shortcuts: | ||
298 | page_description: "Здесь доступны горячие клавиши wallabag." | ||
299 | shortcut: "Горячие клавиши" | ||
300 | action: "Действия" | ||
301 | all_pages_title: "Горячие клавиши на всех страницах" | ||
302 | go_unread: "Перейти в непрочитанные" | ||
303 | go_starred: "Перейти в помеченные звездочкой" | ||
304 | go_archive: "Перейти в архивные" | ||
305 | go_all: "Перейти ко всем записям" | ||
306 | go_tags: "Перейти в теги" | ||
307 | go_config: "Перейти в настройки" | ||
308 | go_import: "Перейти в импорт" | ||
309 | go_developers: "Перейти к разработчикам" | ||
310 | go_howto: "Перейти в howto (эта страница!)" | ||
311 | go_logout: "Выйти" | ||
312 | list_title: "Горячие клавиши, доступные на страницу списков" | ||
313 | search: "Отобразить форму поиска" | ||
314 | article_title: "Горячие клавиши доступные во время просмотра записи" | ||
315 | open_original: "Открыть оригинальную ссылку на запись" | ||
316 | toggle_favorite: "Переключить пометку звездочкой для записи" | ||
317 | toggle_archive: "Переключить пометку о прочтении для записи" | ||
318 | delete: "Удалить запись" | ||
319 | material_title: "Горячие клавиши доступные только в Material теме" | ||
320 | add_link: "Добавить новую запись" | ||
321 | hide_form: "Скрыть текущую форму (поисковую или новой ссылки)" | ||
322 | arrows_navigation: "Навигация по статьям" | ||
323 | open_article: "Отобразить выбранную запись" | ||
324 | |||
325 | quickstart: | ||
326 | page_title: 'Быстрый старт' | ||
327 | more: 'Еще…' | ||
328 | intro: | ||
329 | title: 'Приветствую в wallabag!' | ||
330 | paragraph_1: "Мы будем сопровождать вас при посещении wallabag и покажем вам некоторые функции, которые могут вас заинтересовать." | ||
331 | paragraph_2: 'Следите за нами!' | ||
332 | configure: | ||
333 | title: 'Настроить приложение' | ||
334 | description: 'Чтобы иметь приложение, которое вам подходит, ознакомьтесь с конфигурацией wallabag.' | ||
335 | language: 'Выбрать язык и дизайн' | ||
336 | rss: 'Включить RSS фид' | ||
337 | tagging_rules: 'Создать правило для автоматической установки тегов' | ||
338 | admin: | ||
339 | title: 'Администрирование' | ||
340 | description: 'Как администратор, у Вас есть привилегии на wallabag. Вы можете:' | ||
341 | new_user: 'Создать нового пользователя' | ||
342 | analytics: 'Настроить аналитику' | ||
343 | sharing: 'Включить сервисы для публикации записей' | ||
344 | export: 'Настроить экспорт' | ||
345 | import: 'Настроить импорт' | ||
346 | first_steps: | ||
347 | title: 'Первый шаг' | ||
348 | description: "Теперь wallabag хорошо настроен, пришло время архивировать Интернет. Вы можете нажать на верхний правый знак +, чтобы добавить ссылку." | ||
349 | new_article: 'Сохраните свою первую запись' | ||
350 | unread_articles: 'И добавьте к ней тег!' | ||
351 | migrate: | ||
352 | title: 'Перейти с существующего сервиса' | ||
353 | description: "Вы используете другой сервис? Мы поможем перенести данные на wallabag." | ||
354 | pocket: 'Перейти с Pocket' | ||
355 | wallabag_v1: 'Перейти с wallabag 1 версии' | ||
356 | wallabag_v2: 'Перейти с wallabag 2 версии' | ||
357 | readability: 'Перейти с Readability' | ||
358 | instapaper: 'Перейти с Instapaper' | ||
359 | developer: | ||
360 | title: 'Для разработчиков' | ||
361 | description: 'Мы также подумали о разработчиках: Docker, API, переводы, и т.д.' | ||
362 | create_application: 'Создать ваше стороннее приложение' | ||
363 | use_docker: 'Использовать Docker для установки wallabag' | ||
364 | docs: | ||
365 | title: 'Полная документация' | ||
366 | description: "В wallabag есть так много особенностей. Не стесняйтесь читать руководство, чтобы узнать эти особенности и как их использовать." | ||
367 | annotate: 'Комментирование своей статьи' | ||
368 | export: 'Конвертировать ваши статьи в ePUB или PDF' | ||
369 | search_filters: 'Посмотрите, как Вы можете искать записи с помощью поисковой системы и фильтров' | ||
370 | fetching_errors: 'Что я могу сделать, если во время извлечения записей произошла ошибка?' | ||
371 | all_docs: 'И много других записей!' | ||
372 | support: | ||
373 | title: 'Поддержка' | ||
374 | description: 'Если Вам нужна помощь, мы здесь чтобы помочь Вам.' | ||
375 | github: 'На GitHub' | ||
376 | email: 'По email' | ||
377 | gitter: 'На Gitter' | ||
378 | |||
379 | tag: | ||
380 | page_title: 'Теги' | ||
381 | list: | ||
382 | number_on_the_page: '{0} Теги не найдены.|{1} Найден один тег.|]1,Inf[ Найдено %count% тегов.' | ||
383 | see_untagged_entries: 'Просмотреть записи без тегов' | ||
384 | new: | ||
385 | add: 'Добавить' | ||
386 | placeholder: 'Вы можете добавить несколько тегов, разделенных запятой.' | ||
387 | |||
388 | import: | ||
389 | page_title: 'Импорт' | ||
390 | page_description: 'Добро пожаловать в импортер wallabag. Выберите сервис, из которого вы хотите перенести данные.' | ||
391 | action: | ||
392 | import_contents: 'Импортировать данные' | ||
393 | form: | ||
394 | mark_as_read_title: 'Отметить все, как прочитанное?' | ||
395 | mark_as_read_label: 'Пометить все импортированные записи, как прочитанные' | ||
396 | file_label: 'Файл' | ||
397 | save_label: 'Загрузить файл' | ||
398 | pocket: | ||
399 | page_title: 'Импорт в Pocket' | ||
400 | description: "Функция импрота добавит все ваши данные в формате Pocket. Pocket не позволяет нам извлекать контент из своего сервиса, поэтому содержимое записей будет повторно перезакачана." | ||
401 | config_missing: | ||
402 | description: "Импорт из Pocket не настроен." | ||
403 | admin_message: 'Вам нужно добавить %keyurls% pocket_consumer_key%keyurle%.' | ||
404 | user_message: 'Администратор сервера должен добавить ключ для API Pocket.' | ||
405 | authorize_message: 'Вы можете импортировать свои данные из своего аккаунта на Pocket. Вам просто нужно нажать на кнопку ниже и разрешить приложению подключение к getpocket.com.' | ||
406 | connect_to_pocket: 'Подключиться к Pocket и импортировать данные' | ||
407 | wallabag_v1: | ||
408 | page_title: 'Импорт > Wallabag v1' | ||
409 | description: 'Функция импорта добавит все ваши записи из wallabag v1. На странице конфигурации нажмите "Экспорт JSON" в разделе "Экспорт данных wallabag". У вас появится файл "wallabag-export-1-xxxx-xx-xx.json".' | ||
410 | how_to: 'Выберите свой файл с настройками экспорта wallabag и нажмите кнопку ниже, чтобы загрузить файл и начать импорт.' | ||
411 | wallabag_v2: | ||
412 | page_title: 'Импорт > Wallabag v2' | ||
413 | description: 'Функция импорта добавит все ваши записи wallabag v2. Перейдите ко всем статьям, затем на боковой панели экспорта нажмите "JSON". У вас появится файл со всеми записями "All articles.json".' | ||
414 | readability: | ||
415 | page_title: 'Импорт > Readability' | ||
416 | description: 'Функция импорта добавит все ваши записи для чтения. На странице инструментов (https://www.readability.com/tools/) нажмите "Экспорт ваших данных" в разделе "Экспорт данных". Вы получите электронное письмо для загрузки json (что не заканчивается только .json файлом).' | ||
417 | how_to: 'Выберите экспорт Readability и нажмите кнопку ниже, чтобы загрузить файл и начать импорт.' | ||
418 | worker: | ||
419 | enabled: "Импорт производится асинхронно. После запуска задачи импорта внешний процесс будет обрабатывать задания по одному за раз. Текущая процедура:" | ||
420 | download_images_warning: "Вы включили загрузку изображений для своих записей. В сочетании с обычным импортом может потребоваться много времени, что может привести к ошибке. Мы <strong>настоятельно рекомендуем</strong> включить асинхронный импорт, чтобы избежать ошибок." | ||
421 | firefox: | ||
422 | page_title: 'Импорт > Firefox' | ||
423 | description: "Функция импорта добавит все ваши закладки из Firefox. Просто зайдите в закладки (Ctrl + Maj + O), затем в \"Импорт и резервное копирование\", выберите \"Резервное копирование ...\". Вы получите файл .json." | ||
424 | how_to: "Выберите файл резервной копии закладок и нажмите кнопку ниже, чтобы импортировать его. Обратите внимание, что процесс может занять много времени, поскольку все статьи должны быть загружены." | ||
425 | chrome: | ||
426 | page_title: 'Импорт > Chrome' | ||
427 | description: "Функция импорта добавит все ваши закладки из Chrome. Расположение фйла зависит от операционной системы: <ul><li>На Linux, перейдите в папку <code>~/.config/chromium/Default/</code></li><li>На Windows, должно находиться в <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default</code></li><li>На OS X, должно находиться в <code>~/Library/Application Support/Google/Chrome/Default/Bookmarks</code></li></ul>После того, как перейдете в папку, скопируйте файл <code>Bookmarks</code>. <em><br>Примечание, если у вас есть Chromium вместо Chrome, вам потребуется скорректировать пути соответственно.</em></p>" | ||
428 | how_to: "Выберите файл резервной копии закладок и нажмите кнопку ниже, чтобы импортировать его. Обратите внимание, что процесс может занять много времени, поскольку все статьи должны быть загружены." | ||
429 | instapaper: | ||
430 | page_title: 'Импорт > Instapaper' | ||
431 | description: 'Функция импорта добавит все ваши закладки из Instapaper. На странице настроек (https://www.instapaper.com/user), нажмите на "Скачать .CSV файл" в разделе "Экспорт". CSV файл будет загружен (наподобии "instapaper-export.csv").' | ||
432 | how_to: 'Выберите файл Instapaper и нажмите кнопку ниже, чтобы импортировать его.' | ||
433 | pinboard: | ||
434 | page_title: "Импорт > Pinboard" | ||
435 | description: 'Функция импорта добавит все ваши закладки из Pinboard. На странице резервирования (https://pinboard.in/settings/backup), нажмите на "JSON" в разделе "Закладки". JSON файл будет загружен (наподобии "pinboard_export").' | ||
436 | how_to: 'Выберите файл Pinboard и нажмите кнопку ниже, чтобы импортировать его.' | ||
437 | |||
438 | developer: | ||
439 | page_title: 'Управление клиентским API' | ||
440 | welcome_message: 'Добро пожаловать в API wallabag' | ||
441 | documentation: 'Документация' | ||
442 | how_to_first_app: 'Как создать мое первое приложение' | ||
443 | full_documentation: 'Посмотреть полную документацию по API' | ||
444 | list_methods: 'Список методов API' | ||
445 | clients: | ||
446 | title: 'Клиенты' | ||
447 | create_new: 'Создать нового клиента' | ||
448 | existing_clients: | ||
449 | title: 'Текущие клиенты' | ||
450 | field_id: 'ID клиента' | ||
451 | field_secret: 'secret-код клиента' | ||
452 | field_uris: 'Ссылки перенаправлений' | ||
453 | field_grant_types: 'Разрешенные типы доступа' | ||
454 | no_client: 'Нет клиентов.' | ||
455 | remove: | ||
456 | warn_message_1: 'Вы имеете возможность удалить клиента %name%. Данные клиента будут удалены БЕЗВОЗВРАТНО!' | ||
457 | warn_message_2: "Если вы удалите его, каждое приложение настроенное через этого клиента не сможет авторизоваться в вашем wallabag." | ||
458 | action: 'Удалить клиента %name%' | ||
459 | client: | ||
460 | page_title: 'Управление клиентским API > Новый клиент' | ||
461 | page_description: 'Вы находитесь в разделе создания нового клиента. Пожалуйста заполните поля ниже для перенаправления к вашему приложению.' | ||
462 | form: | ||
463 | name_label: 'Название клиента' | ||
464 | redirect_uris_label: 'Ссылка перенаправления (опционально)' | ||
465 | save_label: 'Создать нового клиента' | ||
466 | action_back: 'Назад' | ||
467 | client_parameter: | ||
468 | page_title: 'Управление клиентским API > Параметры клиента' | ||
469 | page_description: 'Здесь ваши параметры клиента.' | ||
470 | field_name: 'Название клиента' | ||
471 | field_id: 'ID клиента' | ||
472 | field_secret: 'Secret-код клиента' | ||
473 | back: 'Назад' | ||
474 | read_howto: 'Прочитать "как создать мое первое приложение"' | ||
475 | howto: | ||
476 | page_title: 'Управление клиентским API > Как создать мое первое приложение' | ||
477 | description: | ||
478 | paragraph_1: 'Следующие комманды используют библиотеку <a href="https://github.com/jkbrzt/httpie">HTTPie</a>. Убедитесь, что она у вас установлена, прежде чем использовать ее.' | ||
479 | paragraph_2: 'Вам нужен ключ для того, чтобы ваше стороннее приложение и wallabag API могли обмениваться данными.' | ||
480 | paragraph_3: 'Для создания ключа, вам необходимо <a href="%link%">создать нового клиента</a>.' | ||
481 | paragraph_4: 'Теперь создать ключ (замените client_id, client_secret, username and password на ваши значения):' | ||
482 | paragraph_5: 'API вернет рабочую ссылку, наподобии:' | ||
483 | paragraph_6: 'Ключ доступа access_token используется для вызова конечной точки API. Пример:' | ||
484 | paragraph_7: 'Этот вызов вернет все записи для вашего пользователя.' | ||
485 | paragraph_8: 'Если Вы хотите увидеть все конечные точки API, то смотрите <a href="%link%">в нашу документацию по API</a>.' | ||
486 | back: 'Назад' | ||
487 | |||
488 | user: | ||
489 | page_title: "Управление пользователями" | ||
490 | new_user: "Создать нового пользователя" | ||
491 | edit_user: "Изменить существующего пользователя" | ||
492 | description: "Здесь Вы можете управлять всеми пользователями (создание, изменение и удаление)" | ||
493 | list: | ||
494 | actions: "Действия" | ||
495 | edit_action: "Изменить" | ||
496 | yes: "Да" | ||
497 | no: "Нет" | ||
498 | create_new_one: "Создать нового пользователя" | ||
499 | form: | ||
500 | username_label: 'Логин' | ||
501 | name_label: 'Имя' | ||
502 | password_label: 'Пароль' | ||
503 | repeat_new_password_label: 'Повторите новый пароль' | ||
504 | plain_password_label: '????' | ||
505 | email_label: 'Email' | ||
506 | enabled_label: 'Включить' | ||
507 | last_login_label: 'Последний вход' | ||
508 | twofactor_label: "Двухфакторная аутентификация" | ||
509 | save: "Сохранить" | ||
510 | delete: "Удалить" | ||
511 | delete_confirm: "Вы уверены?" | ||
512 | back_to_list: "Назад к списку" | ||
513 | |||
514 | error: | ||
515 | page_title: "Произошла ошибка" | ||
516 | |||
517 | flashes: | ||
518 | config: | ||
519 | notice: | ||
520 | config_saved: 'Настройки сохранены.' | ||
521 | password_updated: 'Пароль обновлен' | ||
522 | password_not_updated_demo: "В режиме демонстрации нельзя изменять пароль для этого пользователя." | ||
523 | user_updated: 'Информация обновлена' | ||
524 | rss_updated: 'RSS информация обновлена' | ||
525 | tagging_rules_updated: 'Правила тегировния обновлены' | ||
526 | tagging_rules_deleted: 'Правила тегировния удалены' | ||
527 | rss_token_updated: 'RSS ключ обновлен' | ||
528 | annotations_reset: "Аннотации сброшены" | ||
529 | tags_reset: "Теги сброшены" | ||
530 | entries_reset: "Записи сброшены" | ||
531 | entry: | ||
532 | notice: | ||
533 | entry_already_saved: 'Запись была сохранена ранее %date%' | ||
534 | entry_saved: 'Запись сохранена' | ||
535 | entry_saved_failed: 'Запись сохранена, но содержимое не удалось получить' | ||
536 | entry_updated: 'Запись обновлена' | ||
537 | entry_reloaded: 'Запись перезагружена' | ||
538 | entry_reloaded_failed: 'Запись перезагружена, но содержимое не удалось получить' | ||
539 | entry_archived: 'Запись перемещена в архив' | ||
540 | entry_unarchived: 'Запись перемещена из архива' | ||
541 | entry_starred: 'Запись помечена звездочкой' | ||
542 | entry_unstarred: 'Пометка звездочкой у записи убрана' | ||
543 | entry_deleted: 'Запись удалена' | ||
544 | tag: | ||
545 | notice: | ||
546 | tag_added: 'Тег добавлен' | ||
547 | import: | ||
548 | notice: | ||
549 | failed: 'Во время импорта произошла ошибка, повторите попытку.' | ||
550 | failed_on_file: 'Ошибка при обработке данных для импорта. Пожалуйста проверьте свой файл импорта.' | ||
551 | summary: 'Статистика импорта: %imported% импортировано, %skipped% уже сохранено.' | ||
552 | summary_with_queue: 'Статистика импорта: %queued% в очереди.' | ||
553 | error: | ||
554 | redis_enabled_not_installed: "Redis включен, для приема асинхронного импорта, но похоже, что <u>мы не можем к нему подключиться</u>. Пожалуйста проверьте настройки Redis." | ||
555 | rabbit_enabled_not_installed: "RabbitMQ включен, для приема асинхронного импорта, но похоже, что <u>мы не можем к нему подключиться</u>. Пожалуйста проверьте настройки RabbitMQ." | ||
556 | developer: | ||
557 | notice: | ||
558 | client_created: 'Новый клиент %name% добавлен.' | ||
559 | client_deleted: 'Клиент %name% удален' | ||
560 | user: | ||
561 | notice: | ||
562 | added: 'Пользователь "%username%" добавлен' | ||
563 | updated: 'Пользователь "%username%" обновлен' | ||
564 | deleted: 'Пользователь "%username%" удален' \ No newline at end of file | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index d3180f42..563bc50b 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: |
@@ -164,6 +169,7 @@ entry: | |||
164 | # filtered_tags: 'Filtered by tags:' | 169 | # filtered_tags: 'Filtered by tags:' |
165 | # filtered_search: 'Filtered by search:' | 170 | # filtered_search: 'Filtered by search:' |
166 | # untagged: 'Untagged entries' | 171 | # untagged: 'Untagged entries' |
172 | # all: 'All entries' | ||
167 | list: | 173 | list: |
168 | number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.' | 174 | number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.' |
169 | reading_time: 'tahmini okuma süresi' | 175 | reading_time: 'tahmini okuma süresi' |
@@ -223,6 +229,8 @@ entry: | |||
223 | original_article: 'orijinal' | 229 | original_article: 'orijinal' |
224 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' | 230 | # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' |
225 | created_at: 'Oluşturulma tarihi' | 231 | created_at: 'Oluşturulma tarihi' |
232 | # published_at: 'Publication date' | ||
233 | # published_by: 'Published by' | ||
226 | new: | 234 | new: |
227 | page_title: 'Yeni makaleyi kaydet' | 235 | page_title: 'Yeni makaleyi kaydet' |
228 | placeholder: 'http://website.com' | 236 | placeholder: 'http://website.com' |
@@ -234,10 +242,12 @@ entry: | |||
234 | page_title: 'Makaleyi düzenle' | 242 | page_title: 'Makaleyi düzenle' |
235 | title_label: 'Başlık' | 243 | title_label: 'Başlık' |
236 | url_label: 'Url' | 244 | url_label: 'Url' |
237 | is_public_label: 'Herkes tarafından erişime açık olsun mu?' | ||
238 | save_label: 'Kaydet' | 245 | save_label: 'Kaydet' |
239 | public: | 246 | public: |
240 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 247 | # shared_by_wallabag: "This article has been shared by %username% with <a href='%wallabag_instance%'>wallabag</a>" |
248 | confirm: | ||
249 | # delete: "Are you sure you want to remove that article?" | ||
250 | # delete_tag: "Are you sure you want to remove that tag from that article?" | ||
241 | 251 | ||
242 | about: | 252 | about: |
243 | page_title: 'Hakkımızda' | 253 | page_title: 'Hakkımızda' |
@@ -385,6 +395,9 @@ tag: | |||
385 | # add: 'Add' | 395 | # add: 'Add' |
386 | # placeholder: 'You can add several tags, separated by a comma.' | 396 | # placeholder: 'You can add several tags, separated by a comma.' |
387 | 397 | ||
398 | # export: | ||
399 | # footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' | ||
400 | |||
388 | import: | 401 | import: |
389 | page_title: 'İçe Aktar' | 402 | page_title: 'İçe Aktar' |
390 | page_description: 'wallabag içe aktarma aracına hoşgeldiniz. Lütfen içe aktarmak istediğiiz önceki servisinizi seçin.' | 403 | page_description: 'wallabag içe aktarma aracına hoşgeldiniz. Lütfen içe aktarmak istediğiiz önceki servisinizi seçin.' |
@@ -510,6 +523,8 @@ user: | |||
510 | # delete: Delete | 523 | # delete: Delete |
511 | # delete_confirm: Are you sure? | 524 | # delete_confirm: Are you sure? |
512 | # back_to_list: Back to list | 525 | # back_to_list: Back to list |
526 | search: | ||
527 | # placeholder: Filter by username or email | ||
513 | 528 | ||
514 | error: | 529 | error: |
515 | # page_title: An error occurred | 530 | # page_title: An error occurred |
@@ -528,6 +543,7 @@ flashes: | |||
528 | # annotations_reset: Annotations reset | 543 | # annotations_reset: Annotations reset |
529 | # tags_reset: Tags reset | 544 | # tags_reset: Tags reset |
530 | # entries_reset: Entries reset | 545 | # entries_reset: Entries reset |
546 | # archived_reset: Archived entries deleted | ||
531 | entry: | 547 | entry: |
532 | notice: | 548 | notice: |
533 | entry_already_saved: 'Entry already saved on %date%' | 549 | entry_already_saved: 'Entry already saved on %date%' |
@@ -562,3 +578,8 @@ flashes: | |||
562 | # added: 'User "%username%" added' | 578 | # added: 'User "%username%" added' |
563 | # updated: 'User "%username%" updated' | 579 | # updated: 'User "%username%" updated' |
564 | # deleted: 'User "%username%" deleted' | 580 | # deleted: 'User "%username%" deleted' |
581 | site_credential: | ||
582 | notice: | ||
583 | # added: 'Site credential for "%host%" added' | ||
584 | # updated: 'Site credential for "%host%" updated' | ||
585 | # 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..87f00f10 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml | |||
@@ -1,6 +1,7 @@ | |||
1 | validator: | 1 | validator: |
2 | password_must_match: 'Cal que los dos senhals siasquen los meteisses' | 2 | password_must_match: 'Cal que los dos senhals correspondan' |
3 | password_too_short: 'Lo senhal deu aver almens 8 caractèrs' | 3 | password_too_short: 'Lo senhal deu aver almens 8 caractèrs' |
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: 'Aquesta citacion es tròpa longa. Cal que faga {{ limit }} caractèrs o mens.' | ||
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.ru.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.ru.yml new file mode 100644 index 00000000..567b8e6e --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.ru.yml | |||
@@ -0,0 +1,6 @@ | |||
1 | validator: | ||
2 | password_must_match: 'Пароли должны совпадать.' | ||
3 | password_too_short: 'Пароль должен быть длиннее 8 символов' | ||
4 | password_wrong_value: 'Неправильный текущий пароль' | ||
5 | item_per_page_too_high: 'Это, безусловно, убьет приложение' | ||
6 | rss_limit_too_high: 'Это, безусловно, убьет приложение' | ||
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..12cead48 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,8 +86,7 @@ | |||
76 | 86 | ||
77 | <!-- Export --> | 87 | <!-- Export --> |
78 | <aside id="download-form"> | 88 | <aside id="download-form"> |
79 | {% set currentRoute = app.request.attributes.get('_route') %} | 89 | {% set currentTag = null %} |
80 | {% set currentTag = '' %} | ||
81 | {% if tag is defined %} | 90 | {% if tag is defined %} |
82 | {% set currentTag = tag %} | 91 | {% set currentTag = tag %} |
83 | {% endif %} | 92 | {% endif %} |
@@ -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 d6b83fd2..f8723189 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/_title.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig index 654c1d2d..5c17e9f7 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig | |||
@@ -5,7 +5,7 @@ | |||
5 | {% elseif currentRoute == 'archive' %} | 5 | {% elseif currentRoute == 'archive' %} |
6 | {{ 'entry.page_titles.archived'|trans }} | 6 | {{ 'entry.page_titles.archived'|trans }} |
7 | {% elseif currentRoute == 'all' %} | 7 | {% elseif currentRoute == 'all' %} |
8 | {{ 'entry.page_titles.filtered'|trans }} | 8 | {{ isFiltered ? 'entry.page_titles.filtered'|trans : 'entry.page_titles.all'|trans }} |
9 | {% elseif currentRoute == 'search' %} | 9 | {% elseif currentRoute == 'search' %} |
10 | {{ 'entry.page_titles.filtered_search'|trans }} {{ filter }} | 10 | {{ 'entry.page_titles.filtered_search'|trans }} {{ filter }} |
11 | {% elseif currentRoute == 'tag_entries' %} | 11 | {% elseif currentRoute == 'tag_entries' %} |
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..9d6fb3f5 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') %} | 60 | {% set currentTag = null %} |
50 | {% 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/CoreBundle/Twig/WallabagExtension.php b/src/Wallabag/CoreBundle/Twig/WallabagExtension.php index a305c53f..351172c4 100644 --- a/src/Wallabag/CoreBundle/Twig/WallabagExtension.php +++ b/src/Wallabag/CoreBundle/Twig/WallabagExtension.php | |||
@@ -3,9 +3,9 @@ | |||
3 | namespace Wallabag\CoreBundle\Twig; | 3 | namespace Wallabag\CoreBundle\Twig; |
4 | 4 | ||
5 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | 5 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
6 | use Symfony\Component\Translation\TranslatorInterface; | ||
6 | use Wallabag\CoreBundle\Repository\EntryRepository; | 7 | use Wallabag\CoreBundle\Repository\EntryRepository; |
7 | use Wallabag\CoreBundle\Repository\TagRepository; | 8 | use Wallabag\CoreBundle\Repository\TagRepository; |
8 | use Symfony\Component\Translation\TranslatorInterface; | ||
9 | 9 | ||
10 | class WallabagExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface | 10 | class WallabagExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface |
11 | { | 11 | { |
@@ -33,11 +33,11 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
33 | 33 | ||
34 | public function getFunctions() | 34 | public function getFunctions() |
35 | { | 35 | { |
36 | return array( | 36 | return [ |
37 | new \Twig_SimpleFunction('count_entries', [$this, 'countEntries']), | 37 | new \Twig_SimpleFunction('count_entries', [$this, 'countEntries']), |
38 | new \Twig_SimpleFunction('count_tags', [$this, 'countTags']), | 38 | new \Twig_SimpleFunction('count_tags', [$this, 'countTags']), |
39 | new \Twig_SimpleFunction('display_stats', [$this, 'displayStats']), | 39 | new \Twig_SimpleFunction('display_stats', [$this, 'displayStats']), |
40 | ); | 40 | ]; |
41 | } | 41 | } |
42 | 42 | ||
43 | public function removeWww($url) | 43 | public function removeWww($url) |
@@ -64,19 +64,15 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
64 | case 'starred': | 64 | case 'starred': |
65 | $qb = $this->entryRepository->getBuilderForStarredByUser($user->getId()); | 65 | $qb = $this->entryRepository->getBuilderForStarredByUser($user->getId()); |
66 | break; | 66 | break; |
67 | |||
68 | case 'archive': | 67 | case 'archive': |
69 | $qb = $this->entryRepository->getBuilderForArchiveByUser($user->getId()); | 68 | $qb = $this->entryRepository->getBuilderForArchiveByUser($user->getId()); |
70 | break; | 69 | break; |
71 | |||
72 | case 'unread': | 70 | case 'unread': |
73 | $qb = $this->entryRepository->getBuilderForUnreadByUser($user->getId()); | 71 | $qb = $this->entryRepository->getBuilderForUnreadByUser($user->getId()); |
74 | break; | 72 | break; |
75 | |||
76 | case 'all': | 73 | case 'all': |
77 | $qb = $this->entryRepository->getBuilderForAllByUser($user->getId()); | 74 | $qb = $this->entryRepository->getBuilderForAllByUser($user->getId()); |
78 | break; | 75 | break; |
79 | |||
80 | default: | 76 | default: |
81 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); | 77 | throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); |
82 | } | 78 | } |
@@ -139,7 +135,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa | |||
139 | $nbDays = (int) $interval->format('%a') ?: 1; | 135 | $nbDays = (int) $interval->format('%a') ?: 1; |
140 | 136 | ||
141 | // force setlocale for date translation | 137 | // force setlocale for date translation |
142 | setlocale(LC_TIME, strtolower($user->getConfig()->getLanguage()).'_'.strtoupper(strtolower($user->getConfig()->getLanguage()))); | 138 | setlocale(LC_TIME, strtolower($user->getConfig()->getLanguage()) . '_' . strtoupper(strtolower($user->getConfig()->getLanguage()))); |
143 | 139 | ||
144 | return $this->translator->trans('footer.stats', [ | 140 | return $this->translator->trans('footer.stats', [ |
145 | '%user_creation%' => strftime('%e %B %Y', $user->getCreatedAt()->getTimestamp()), | 141 | '%user_creation%' => strftime('%e %B %Y', $user->getCreatedAt()->getTimestamp()), |
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php index 28d01715..99056c2c 100644 --- a/src/Wallabag/ImportBundle/Command/ImportCommand.php +++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php | |||
@@ -6,6 +6,7 @@ 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\InputInterface; | 8 | use Symfony\Component\Console\Input\InputInterface; |
9 | use Symfony\Component\Console\Input\InputOption; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | 10 | use Symfony\Component\Console\Output\OutputInterface; |
10 | 11 | ||
11 | class ImportCommand extends ContainerAwareCommand | 12 | class ImportCommand extends ContainerAwareCommand |
@@ -15,16 +16,18 @@ 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 | ||
25 | protected function execute(InputInterface $input, OutputInterface $output) | 28 | protected function execute(InputInterface $input, OutputInterface $output) |
26 | { | 29 | { |
27 | $output->writeln('Start : '.(new \DateTime())->format('d-m-Y G:i:s').' ---'); | 30 | $output->writeln('Start : ' . (new \DateTime())->format('d-m-Y G:i:s') . ' ---'); |
28 | 31 | ||
29 | if (!file_exists($input->getArgument('filepath'))) { | 32 | if (!file_exists($input->getArgument('filepath'))) { |
30 | throw new Exception(sprintf('File "%s" not found', $input->getArgument('filepath'))); | 33 | throw new Exception(sprintf('File "%s" not found', $input->getArgument('filepath'))); |
@@ -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 |
@@ -72,12 +80,12 @@ class ImportCommand extends ContainerAwareCommand | |||
72 | 80 | ||
73 | if (true === $res) { | 81 | if (true === $res) { |
74 | $summary = $import->getSummary(); | 82 | $summary = $import->getSummary(); |
75 | $output->writeln('<info>'.$summary['imported'].' imported</info>'); | 83 | $output->writeln('<info>' . $summary['imported'] . ' imported</info>'); |
76 | $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>'); | 84 | $output->writeln('<comment>' . $summary['skipped'] . ' already saved</comment>'); |
77 | } | 85 | } |
78 | 86 | ||
79 | $em->clear(); | 87 | $em->clear(); |
80 | 88 | ||
81 | $output->writeln('End : '.(new \DateTime())->format('d-m-Y G:i:s').' ---'); | 89 | $output->writeln('End : ' . (new \DateTime())->format('d-m-Y G:i:s') . ' ---'); |
82 | } | 90 | } |
83 | } | 91 | } |
diff --git a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php index 2d06af44..d94900ad 100644 --- a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php +++ b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php | |||
@@ -2,13 +2,13 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Command; | 3 | namespace Wallabag\ImportBundle\Command; |
4 | 4 | ||
5 | use Simpleue\Worker\QueueWorker; | ||
5 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | 6 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; |
6 | use Symfony\Component\Config\Definition\Exception\Exception; | 7 | use Symfony\Component\Config\Definition\Exception\Exception; |
7 | use Symfony\Component\Console\Input\InputArgument; | 8 | use Symfony\Component\Console\Input\InputArgument; |
8 | use Symfony\Component\Console\Input\InputOption; | ||
9 | use Symfony\Component\Console\Input\InputInterface; | 9 | use Symfony\Component\Console\Input\InputInterface; |
10 | use Symfony\Component\Console\Input\InputOption; | ||
10 | use Symfony\Component\Console\Output\OutputInterface; | 11 | use Symfony\Component\Console\Output\OutputInterface; |
11 | use Simpleue\Worker\QueueWorker; | ||
12 | 12 | ||
13 | class RedisWorkerCommand extends ContainerAwareCommand | 13 | class RedisWorkerCommand extends ContainerAwareCommand |
14 | { | 14 | { |
@@ -24,18 +24,18 @@ class RedisWorkerCommand extends ContainerAwareCommand | |||
24 | 24 | ||
25 | protected function execute(InputInterface $input, OutputInterface $output) | 25 | protected function execute(InputInterface $input, OutputInterface $output) |
26 | { | 26 | { |
27 | $output->writeln('Worker started at: '.(new \DateTime())->format('d-m-Y G:i:s')); | 27 | $output->writeln('Worker started at: ' . (new \DateTime())->format('d-m-Y G:i:s')); |
28 | $output->writeln('Waiting for message ...'); | 28 | $output->writeln('Waiting for message ...'); |
29 | 29 | ||
30 | $serviceName = $input->getArgument('serviceName'); | 30 | $serviceName = $input->getArgument('serviceName'); |
31 | 31 | ||
32 | if (!$this->getContainer()->has('wallabag_import.queue.redis.'.$serviceName) || !$this->getContainer()->has('wallabag_import.consumer.redis.'.$serviceName)) { | 32 | if (!$this->getContainer()->has('wallabag_import.queue.redis.' . $serviceName) || !$this->getContainer()->has('wallabag_import.consumer.redis.' . $serviceName)) { |
33 | throw new Exception(sprintf('No queue or consumer found for service name: "%s"', $input->getArgument('serviceName'))); | 33 | throw new Exception(sprintf('No queue or consumer found for service name: "%s"', $input->getArgument('serviceName'))); |
34 | } | 34 | } |
35 | 35 | ||
36 | $worker = new QueueWorker( | 36 | $worker = new QueueWorker( |
37 | $this->getContainer()->get('wallabag_import.queue.redis.'.$serviceName), | 37 | $this->getContainer()->get('wallabag_import.queue.redis.' . $serviceName), |
38 | $this->getContainer()->get('wallabag_import.consumer.redis.'.$serviceName), | 38 | $this->getContainer()->get('wallabag_import.consumer.redis.' . $serviceName), |
39 | (int) $input->getOption('maxIterations') | 39 | (int) $input->getOption('maxIterations') |
40 | ); | 40 | ); |
41 | 41 | ||
diff --git a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php index 992ce1ad..b035f5cc 100644 --- a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php +++ b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php | |||
@@ -3,14 +3,14 @@ | |||
3 | namespace Wallabag\ImportBundle\Consumer; | 3 | namespace Wallabag\ImportBundle\Consumer; |
4 | 4 | ||
5 | use Doctrine\ORM\EntityManager; | 5 | use Doctrine\ORM\EntityManager; |
6 | use Wallabag\ImportBundle\Import\AbstractImport; | ||
7 | use Wallabag\UserBundle\Repository\UserRepository; | ||
8 | use Wallabag\CoreBundle\Entity\Entry; | ||
9 | use Wallabag\CoreBundle\Entity\Tag; | ||
10 | use Psr\Log\LoggerInterface; | 6 | use Psr\Log\LoggerInterface; |
11 | use Psr\Log\NullLogger; | 7 | use Psr\Log\NullLogger; |
12 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; | 8 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
9 | use Wallabag\CoreBundle\Entity\Entry; | ||
10 | use Wallabag\CoreBundle\Entity\Tag; | ||
13 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | 11 | use Wallabag\CoreBundle\Event\EntrySavedEvent; |
12 | use Wallabag\ImportBundle\Import\AbstractImport; | ||
13 | use Wallabag\UserBundle\Repository\UserRepository; | ||
14 | 14 | ||
15 | abstract class AbstractConsumer | 15 | abstract class AbstractConsumer |
16 | { | 16 | { |
@@ -76,7 +76,7 @@ abstract class AbstractConsumer | |||
76 | return false; | 76 | return false; |
77 | } | 77 | } |
78 | 78 | ||
79 | $this->logger->info('Content with url imported! ('.$entry->getUrl().')'); | 79 | $this->logger->info('Content with url imported! (' . $entry->getUrl() . ')'); |
80 | 80 | ||
81 | return true; | 81 | return true; |
82 | } | 82 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/BrowserController.php b/src/Wallabag/ImportBundle/Controller/BrowserController.php index e119098f..77a7a904 100644 --- a/src/Wallabag/ImportBundle/Controller/BrowserController.php +++ b/src/Wallabag/ImportBundle/Controller/BrowserController.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Component\HttpFoundation\Response; | 8 | use Symfony\Component\HttpFoundation\Response; |
9 | use Wallabag\ImportBundle\Form\Type\UploadImportType; | 9 | use Wallabag\ImportBundle\Form\Type\UploadImportType; |
@@ -11,20 +11,6 @@ use Wallabag\ImportBundle\Form\Type\UploadImportType; | |||
11 | abstract class BrowserController extends Controller | 11 | abstract class BrowserController extends Controller |
12 | { | 12 | { |
13 | /** | 13 | /** |
14 | * Return the service to handle the import. | ||
15 | * | ||
16 | * @return \Wallabag\ImportBundle\Import\ImportInterface | ||
17 | */ | ||
18 | abstract protected function getImportService(); | ||
19 | |||
20 | /** | ||
21 | * Return the template used for the form. | ||
22 | * | ||
23 | * @return string | ||
24 | */ | ||
25 | abstract protected function getImportTemplate(); | ||
26 | |||
27 | /** | ||
28 | * @Route("/browser", name="import_browser") | 14 | * @Route("/browser", name="import_browser") |
29 | * | 15 | * |
30 | * @param Request $request | 16 | * @param Request $request |
@@ -42,11 +28,11 @@ abstract class BrowserController extends Controller | |||
42 | if ($form->isSubmitted() && $form->isValid()) { | 28 | if ($form->isSubmitted() && $form->isValid()) { |
43 | $file = $form->get('file')->getData(); | 29 | $file = $form->get('file')->getData(); |
44 | $markAsRead = $form->get('mark_as_read')->getData(); | 30 | $markAsRead = $form->get('mark_as_read')->getData(); |
45 | $name = $this->getUser()->getId().'.json'; | 31 | $name = $this->getUser()->getId() . '.json'; |
46 | 32 | ||
47 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { | 33 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { |
48 | $res = $wallabag | 34 | $res = $wallabag |
49 | ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) | 35 | ->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name) |
50 | ->setMarkAsRead($markAsRead) | 36 | ->setMarkAsRead($markAsRead) |
51 | ->import(); | 37 | ->import(); |
52 | 38 | ||
@@ -65,7 +51,7 @@ abstract class BrowserController extends Controller | |||
65 | ]); | 51 | ]); |
66 | } | 52 | } |
67 | 53 | ||
68 | unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); | 54 | unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name); |
69 | } | 55 | } |
70 | 56 | ||
71 | $this->get('session')->getFlashBag()->add( | 57 | $this->get('session')->getFlashBag()->add( |
@@ -74,12 +60,11 @@ abstract class BrowserController extends Controller | |||
74 | ); | 60 | ); |
75 | 61 | ||
76 | return $this->redirect($this->generateUrl('homepage')); | 62 | return $this->redirect($this->generateUrl('homepage')); |
77 | } else { | 63 | } |
78 | $this->get('session')->getFlashBag()->add( | 64 | $this->get('session')->getFlashBag()->add( |
79 | 'notice', | 65 | 'notice', |
80 | 'flashes.import.notice.failed_on_file' | 66 | 'flashes.import.notice.failed_on_file' |
81 | ); | 67 | ); |
82 | } | ||
83 | } | 68 | } |
84 | 69 | ||
85 | return $this->render($this->getImportTemplate(), [ | 70 | return $this->render($this->getImportTemplate(), [ |
@@ -87,4 +72,18 @@ abstract class BrowserController extends Controller | |||
87 | 'import' => $wallabag, | 72 | 'import' => $wallabag, |
88 | ]); | 73 | ]); |
89 | } | 74 | } |
75 | |||
76 | /** | ||
77 | * Return the service to handle the import. | ||
78 | * | ||
79 | * @return \Wallabag\ImportBundle\Import\ImportInterface | ||
80 | */ | ||
81 | abstract protected function getImportService(); | ||
82 | |||
83 | /** | ||
84 | * Return the template used for the form. | ||
85 | * | ||
86 | * @return string | ||
87 | */ | ||
88 | abstract protected function getImportTemplate(); | ||
90 | } | 89 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/ChromeController.php b/src/Wallabag/ImportBundle/Controller/ChromeController.php index 454f3347..0cb418a1 100644 --- a/src/Wallabag/ImportBundle/Controller/ChromeController.php +++ b/src/Wallabag/ImportBundle/Controller/ChromeController.php | |||
@@ -8,6 +8,14 @@ use Symfony\Component\HttpFoundation\Request; | |||
8 | class ChromeController extends BrowserController | 8 | class ChromeController extends BrowserController |
9 | { | 9 | { |
10 | /** | 10 | /** |
11 | * @Route("/chrome", name="import_chrome") | ||
12 | */ | ||
13 | public function indexAction(Request $request) | ||
14 | { | ||
15 | return parent::indexAction($request); | ||
16 | } | ||
17 | |||
18 | /** | ||
11 | * {@inheritdoc} | 19 | * {@inheritdoc} |
12 | */ | 20 | */ |
13 | protected function getImportService() | 21 | protected function getImportService() |
@@ -30,12 +38,4 @@ class ChromeController extends BrowserController | |||
30 | { | 38 | { |
31 | return 'WallabagImportBundle:Chrome:index.html.twig'; | 39 | return 'WallabagImportBundle:Chrome:index.html.twig'; |
32 | } | 40 | } |
33 | |||
34 | /** | ||
35 | * @Route("/chrome", name="import_chrome") | ||
36 | */ | ||
37 | public function indexAction(Request $request) | ||
38 | { | ||
39 | return parent::indexAction($request); | ||
40 | } | ||
41 | } | 41 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/FirefoxController.php b/src/Wallabag/ImportBundle/Controller/FirefoxController.php index c329b9c4..88697f9d 100644 --- a/src/Wallabag/ImportBundle/Controller/FirefoxController.php +++ b/src/Wallabag/ImportBundle/Controller/FirefoxController.php | |||
@@ -8,6 +8,14 @@ use Symfony\Component\HttpFoundation\Request; | |||
8 | class FirefoxController extends BrowserController | 8 | class FirefoxController extends BrowserController |
9 | { | 9 | { |
10 | /** | 10 | /** |
11 | * @Route("/firefox", name="import_firefox") | ||
12 | */ | ||
13 | public function indexAction(Request $request) | ||
14 | { | ||
15 | return parent::indexAction($request); | ||
16 | } | ||
17 | |||
18 | /** | ||
11 | * {@inheritdoc} | 19 | * {@inheritdoc} |
12 | */ | 20 | */ |
13 | protected function getImportService() | 21 | protected function getImportService() |
@@ -30,12 +38,4 @@ class FirefoxController extends BrowserController | |||
30 | { | 38 | { |
31 | return 'WallabagImportBundle:Firefox:index.html.twig'; | 39 | return 'WallabagImportBundle:Firefox:index.html.twig'; |
32 | } | 40 | } |
33 | |||
34 | /** | ||
35 | * @Route("/firefox", name="import_firefox") | ||
36 | */ | ||
37 | public function indexAction(Request $request) | ||
38 | { | ||
39 | return parent::indexAction($request); | ||
40 | } | ||
41 | } | 41 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/ImportController.php b/src/Wallabag/ImportBundle/Controller/ImportController.php index 237c748e..7e4fd174 100644 --- a/src/Wallabag/ImportBundle/Controller/ImportController.php +++ b/src/Wallabag/ImportBundle/Controller/ImportController.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
7 | 7 | ||
8 | class ImportController extends Controller | 8 | class ImportController extends Controller |
9 | { | 9 | { |
@@ -86,9 +86,9 @@ class ImportController extends Controller | |||
86 | private function getTotalMessageInRabbitQueue($importService) | 86 | private function getTotalMessageInRabbitQueue($importService) |
87 | { | 87 | { |
88 | $message = $this | 88 | $message = $this |
89 | ->get('old_sound_rabbit_mq.import_'.$importService.'_consumer') | 89 | ->get('old_sound_rabbit_mq.import_' . $importService . '_consumer') |
90 | ->getChannel() | 90 | ->getChannel() |
91 | ->basic_get('wallabag.import.'.$importService); | 91 | ->basic_get('wallabag.import.' . $importService); |
92 | 92 | ||
93 | if (null === $message) { | 93 | if (null === $message) { |
94 | return 0; | 94 | return 0; |
diff --git a/src/Wallabag/ImportBundle/Controller/InstapaperController.php b/src/Wallabag/ImportBundle/Controller/InstapaperController.php index 0251acb9..550679c3 100644 --- a/src/Wallabag/ImportBundle/Controller/InstapaperController.php +++ b/src/Wallabag/ImportBundle/Controller/InstapaperController.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Wallabag\ImportBundle\Form\Type\UploadImportType; | 8 | use Wallabag\ImportBundle\Form\Type\UploadImportType; |
9 | 9 | ||
@@ -29,11 +29,11 @@ class InstapaperController extends Controller | |||
29 | if ($form->isSubmitted() && $form->isValid()) { | 29 | if ($form->isSubmitted() && $form->isValid()) { |
30 | $file = $form->get('file')->getData(); | 30 | $file = $form->get('file')->getData(); |
31 | $markAsRead = $form->get('mark_as_read')->getData(); | 31 | $markAsRead = $form->get('mark_as_read')->getData(); |
32 | $name = 'instapaper_'.$this->getUser()->getId().'.csv'; | 32 | $name = 'instapaper_' . $this->getUser()->getId() . '.csv'; |
33 | 33 | ||
34 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { | 34 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { |
35 | $res = $instapaper | 35 | $res = $instapaper |
36 | ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) | 36 | ->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name) |
37 | ->setMarkAsRead($markAsRead) | 37 | ->setMarkAsRead($markAsRead) |
38 | ->import(); | 38 | ->import(); |
39 | 39 | ||
@@ -52,7 +52,7 @@ class InstapaperController extends Controller | |||
52 | ]); | 52 | ]); |
53 | } | 53 | } |
54 | 54 | ||
55 | unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); | 55 | unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name); |
56 | } | 56 | } |
57 | 57 | ||
58 | $this->get('session')->getFlashBag()->add( | 58 | $this->get('session')->getFlashBag()->add( |
@@ -61,12 +61,12 @@ class InstapaperController extends Controller | |||
61 | ); | 61 | ); |
62 | 62 | ||
63 | return $this->redirect($this->generateUrl('homepage')); | 63 | return $this->redirect($this->generateUrl('homepage')); |
64 | } else { | ||
65 | $this->get('session')->getFlashBag()->add( | ||
66 | 'notice', | ||
67 | 'flashes.import.notice.failed_on_file' | ||
68 | ); | ||
69 | } | 64 | } |
65 | |||
66 | $this->get('session')->getFlashBag()->add( | ||
67 | 'notice', | ||
68 | 'flashes.import.notice.failed_on_file' | ||
69 | ); | ||
70 | } | 70 | } |
71 | 71 | ||
72 | return $this->render('WallabagImportBundle:Instapaper:index.html.twig', [ | 72 | return $this->render('WallabagImportBundle:Instapaper:index.html.twig', [ |
diff --git a/src/Wallabag/ImportBundle/Controller/PinboardController.php b/src/Wallabag/ImportBundle/Controller/PinboardController.php index d0ad8aa8..0e57fd41 100644 --- a/src/Wallabag/ImportBundle/Controller/PinboardController.php +++ b/src/Wallabag/ImportBundle/Controller/PinboardController.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Wallabag\ImportBundle\Form\Type\UploadImportType; | 8 | use Wallabag\ImportBundle\Form\Type\UploadImportType; |
9 | 9 | ||
@@ -29,11 +29,11 @@ class PinboardController extends Controller | |||
29 | if ($form->isSubmitted() && $form->isValid()) { | 29 | if ($form->isSubmitted() && $form->isValid()) { |
30 | $file = $form->get('file')->getData(); | 30 | $file = $form->get('file')->getData(); |
31 | $markAsRead = $form->get('mark_as_read')->getData(); | 31 | $markAsRead = $form->get('mark_as_read')->getData(); |
32 | $name = 'pinboard_'.$this->getUser()->getId().'.json'; | 32 | $name = 'pinboard_' . $this->getUser()->getId() . '.json'; |
33 | 33 | ||
34 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { | 34 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { |
35 | $res = $pinboard | 35 | $res = $pinboard |
36 | ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) | 36 | ->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name) |
37 | ->setMarkAsRead($markAsRead) | 37 | ->setMarkAsRead($markAsRead) |
38 | ->import(); | 38 | ->import(); |
39 | 39 | ||
@@ -52,7 +52,7 @@ class PinboardController extends Controller | |||
52 | ]); | 52 | ]); |
53 | } | 53 | } |
54 | 54 | ||
55 | unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); | 55 | unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name); |
56 | } | 56 | } |
57 | 57 | ||
58 | $this->get('session')->getFlashBag()->add( | 58 | $this->get('session')->getFlashBag()->add( |
@@ -61,12 +61,12 @@ class PinboardController extends Controller | |||
61 | ); | 61 | ); |
62 | 62 | ||
63 | return $this->redirect($this->generateUrl('homepage')); | 63 | return $this->redirect($this->generateUrl('homepage')); |
64 | } else { | ||
65 | $this->get('session')->getFlashBag()->add( | ||
66 | 'notice', | ||
67 | 'flashes.import.notice.failed_on_file' | ||
68 | ); | ||
69 | } | 64 | } |
65 | |||
66 | $this->get('session')->getFlashBag()->add( | ||
67 | 'notice', | ||
68 | 'flashes.import.notice.failed_on_file' | ||
69 | ); | ||
70 | } | 70 | } |
71 | 71 | ||
72 | return $this->render('WallabagImportBundle:Pinboard:index.html.twig', [ | 72 | return $this->render('WallabagImportBundle:Pinboard:index.html.twig', [ |
diff --git a/src/Wallabag/ImportBundle/Controller/PocketController.php b/src/Wallabag/ImportBundle/Controller/PocketController.php index 56be5cbf..9f28819a 100644 --- a/src/Wallabag/ImportBundle/Controller/PocketController.php +++ b/src/Wallabag/ImportBundle/Controller/PocketController.php | |||
@@ -2,34 +2,15 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
6 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
7 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
8 | use Symfony\Component\HttpFoundation\Request; | 6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
9 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | 7 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; |
8 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
10 | 10 | ||
11 | class PocketController extends Controller | 11 | class PocketController extends Controller |
12 | { | 12 | { |
13 | /** | 13 | /** |
14 | * Return Pocket Import Service with or without RabbitMQ enabled. | ||
15 | * | ||
16 | * @return \Wallabag\ImportBundle\Import\PocketImport | ||
17 | */ | ||
18 | private function getPocketImportService() | ||
19 | { | ||
20 | $pocket = $this->get('wallabag_import.pocket.import'); | ||
21 | $pocket->setUser($this->getUser()); | ||
22 | |||
23 | if ($this->get('craue_config')->get('import_with_rabbitmq')) { | ||
24 | $pocket->setProducer($this->get('old_sound_rabbit_mq.import_pocket_producer')); | ||
25 | } elseif ($this->get('craue_config')->get('import_with_redis')) { | ||
26 | $pocket->setProducer($this->get('wallabag_import.producer.redis.pocket')); | ||
27 | } | ||
28 | |||
29 | return $pocket; | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * @Route("/pocket", name="import_pocket") | 14 | * @Route("/pocket", name="import_pocket") |
34 | */ | 15 | */ |
35 | public function indexAction() | 16 | public function indexAction() |
@@ -70,7 +51,7 @@ class PocketController extends Controller | |||
70 | $this->get('session')->set('mark_as_read', $request->request->get('form')['mark_as_read']); | 51 | $this->get('session')->set('mark_as_read', $request->request->get('form')['mark_as_read']); |
71 | 52 | ||
72 | return $this->redirect( | 53 | return $this->redirect( |
73 | 'https://getpocket.com/auth/authorize?request_token='.$requestToken.'&redirect_uri='.$this->generateUrl('import_pocket_callback', [], UrlGeneratorInterface::ABSOLUTE_URL), | 54 | 'https://getpocket.com/auth/authorize?request_token=' . $requestToken . '&redirect_uri=' . $this->generateUrl('import_pocket_callback', [], UrlGeneratorInterface::ABSOLUTE_URL), |
74 | 301 | 55 | 301 |
75 | ); | 56 | ); |
76 | } | 57 | } |
@@ -117,4 +98,23 @@ class PocketController extends Controller | |||
117 | 98 | ||
118 | return $this->redirect($this->generateUrl('homepage')); | 99 | return $this->redirect($this->generateUrl('homepage')); |
119 | } | 100 | } |
101 | |||
102 | /** | ||
103 | * Return Pocket Import Service with or without RabbitMQ enabled. | ||
104 | * | ||
105 | * @return \Wallabag\ImportBundle\Import\PocketImport | ||
106 | */ | ||
107 | private function getPocketImportService() | ||
108 | { | ||
109 | $pocket = $this->get('wallabag_import.pocket.import'); | ||
110 | $pocket->setUser($this->getUser()); | ||
111 | |||
112 | if ($this->get('craue_config')->get('import_with_rabbitmq')) { | ||
113 | $pocket->setProducer($this->get('old_sound_rabbit_mq.import_pocket_producer')); | ||
114 | } elseif ($this->get('craue_config')->get('import_with_redis')) { | ||
115 | $pocket->setProducer($this->get('wallabag_import.producer.redis.pocket')); | ||
116 | } | ||
117 | |||
118 | return $pocket; | ||
119 | } | ||
120 | } | 120 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php index aa732ddd..59de24cb 100644 --- a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php +++ b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
6 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Wallabag\ImportBundle\Form\Type\UploadImportType; | 8 | use Wallabag\ImportBundle\Form\Type\UploadImportType; |
9 | 9 | ||
@@ -29,11 +29,11 @@ class ReadabilityController extends Controller | |||
29 | if ($form->isSubmitted() && $form->isValid()) { | 29 | if ($form->isSubmitted() && $form->isValid()) { |
30 | $file = $form->get('file')->getData(); | 30 | $file = $form->get('file')->getData(); |
31 | $markAsRead = $form->get('mark_as_read')->getData(); | 31 | $markAsRead = $form->get('mark_as_read')->getData(); |
32 | $name = 'readability_'.$this->getUser()->getId().'.json'; | 32 | $name = 'readability_' . $this->getUser()->getId() . '.json'; |
33 | 33 | ||
34 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { | 34 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { |
35 | $res = $readability | 35 | $res = $readability |
36 | ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) | 36 | ->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name) |
37 | ->setMarkAsRead($markAsRead) | 37 | ->setMarkAsRead($markAsRead) |
38 | ->import(); | 38 | ->import(); |
39 | 39 | ||
@@ -52,7 +52,7 @@ class ReadabilityController extends Controller | |||
52 | ]); | 52 | ]); |
53 | } | 53 | } |
54 | 54 | ||
55 | unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); | 55 | unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name); |
56 | } | 56 | } |
57 | 57 | ||
58 | $this->get('session')->getFlashBag()->add( | 58 | $this->get('session')->getFlashBag()->add( |
@@ -61,12 +61,12 @@ class ReadabilityController extends Controller | |||
61 | ); | 61 | ); |
62 | 62 | ||
63 | return $this->redirect($this->generateUrl('homepage')); | 63 | return $this->redirect($this->generateUrl('homepage')); |
64 | } else { | ||
65 | $this->get('session')->getFlashBag()->add( | ||
66 | 'notice', | ||
67 | 'flashes.import.notice.failed_on_file' | ||
68 | ); | ||
69 | } | 64 | } |
65 | |||
66 | $this->get('session')->getFlashBag()->add( | ||
67 | 'notice', | ||
68 | 'flashes.import.notice.failed_on_file' | ||
69 | ); | ||
70 | } | 70 | } |
71 | 71 | ||
72 | return $this->render('WallabagImportBundle:Readability:index.html.twig', [ | 72 | return $this->render('WallabagImportBundle:Readability:index.html.twig', [ |
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagController.php b/src/Wallabag/ImportBundle/Controller/WallabagController.php index e81c1ca9..6e6524b4 100644 --- a/src/Wallabag/ImportBundle/Controller/WallabagController.php +++ b/src/Wallabag/ImportBundle/Controller/WallabagController.php | |||
@@ -3,7 +3,9 @@ | |||
3 | namespace Wallabag\ImportBundle\Controller; | 3 | namespace Wallabag\ImportBundle\Controller; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 5 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
6 | use Symfony\Component\HttpFoundation\RedirectResponse; | ||
6 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Component\HttpFoundation\Response; | ||
7 | use Wallabag\ImportBundle\Form\Type\UploadImportType; | 9 | use Wallabag\ImportBundle\Form\Type\UploadImportType; |
8 | 10 | ||
9 | /** | 11 | /** |
@@ -12,20 +14,6 @@ use Wallabag\ImportBundle\Form\Type\UploadImportType; | |||
12 | abstract class WallabagController extends Controller | 14 | abstract class WallabagController extends Controller |
13 | { | 15 | { |
14 | /** | 16 | /** |
15 | * Return the service to handle the import. | ||
16 | * | ||
17 | * @return \Wallabag\ImportBundle\Import\ImportInterface | ||
18 | */ | ||
19 | abstract protected function getImportService(); | ||
20 | |||
21 | /** | ||
22 | * Return the template used for the form. | ||
23 | * | ||
24 | * @return string | ||
25 | */ | ||
26 | abstract protected function getImportTemplate(); | ||
27 | |||
28 | /** | ||
29 | * Handle import request. | 17 | * Handle import request. |
30 | * | 18 | * |
31 | * @param Request $request | 19 | * @param Request $request |
@@ -43,11 +31,11 @@ abstract class WallabagController extends Controller | |||
43 | if ($form->isSubmitted() && $form->isValid()) { | 31 | if ($form->isSubmitted() && $form->isValid()) { |
44 | $file = $form->get('file')->getData(); | 32 | $file = $form->get('file')->getData(); |
45 | $markAsRead = $form->get('mark_as_read')->getData(); | 33 | $markAsRead = $form->get('mark_as_read')->getData(); |
46 | $name = $this->getUser()->getId().'.json'; | 34 | $name = $this->getUser()->getId() . '.json'; |
47 | 35 | ||
48 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { | 36 | if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { |
49 | $res = $wallabag | 37 | $res = $wallabag |
50 | ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) | 38 | ->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name) |
51 | ->setMarkAsRead($markAsRead) | 39 | ->setMarkAsRead($markAsRead) |
52 | ->import(); | 40 | ->import(); |
53 | 41 | ||
@@ -66,7 +54,7 @@ abstract class WallabagController extends Controller | |||
66 | ]); | 54 | ]); |
67 | } | 55 | } |
68 | 56 | ||
69 | unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); | 57 | unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name); |
70 | } | 58 | } |
71 | 59 | ||
72 | $this->get('session')->getFlashBag()->add( | 60 | $this->get('session')->getFlashBag()->add( |
@@ -75,12 +63,12 @@ abstract class WallabagController extends Controller | |||
75 | ); | 63 | ); |
76 | 64 | ||
77 | return $this->redirect($this->generateUrl('homepage')); | 65 | return $this->redirect($this->generateUrl('homepage')); |
78 | } else { | ||
79 | $this->get('session')->getFlashBag()->add( | ||
80 | 'notice', | ||
81 | 'flashes.import.notice.failed_on_file' | ||
82 | ); | ||
83 | } | 66 | } |
67 | |||
68 | $this->get('session')->getFlashBag()->add( | ||
69 | 'notice', | ||
70 | 'flashes.import.notice.failed_on_file' | ||
71 | ); | ||
84 | } | 72 | } |
85 | 73 | ||
86 | return $this->render($this->getImportTemplate(), [ | 74 | return $this->render($this->getImportTemplate(), [ |
@@ -88,4 +76,18 @@ abstract class WallabagController extends Controller | |||
88 | 'import' => $wallabag, | 76 | 'import' => $wallabag, |
89 | ]); | 77 | ]); |
90 | } | 78 | } |
79 | |||
80 | /** | ||
81 | * Return the service to handle the import. | ||
82 | * | ||
83 | * @return \Wallabag\ImportBundle\Import\ImportInterface | ||
84 | */ | ||
85 | abstract protected function getImportService(); | ||
86 | |||
87 | /** | ||
88 | * Return the template used for the form. | ||
89 | * | ||
90 | * @return string | ||
91 | */ | ||
92 | abstract protected function getImportTemplate(); | ||
91 | } | 93 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php b/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php index 312c7a35..d700d8a8 100644 --- a/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php +++ b/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php | |||
@@ -8,6 +8,14 @@ use Symfony\Component\HttpFoundation\Request; | |||
8 | class WallabagV1Controller extends WallabagController | 8 | class WallabagV1Controller extends WallabagController |
9 | { | 9 | { |
10 | /** | 10 | /** |
11 | * @Route("/wallabag-v1", name="import_wallabag_v1") | ||
12 | */ | ||
13 | public function indexAction(Request $request) | ||
14 | { | ||
15 | return parent::indexAction($request); | ||
16 | } | ||
17 | |||
18 | /** | ||
11 | * {@inheritdoc} | 19 | * {@inheritdoc} |
12 | */ | 20 | */ |
13 | protected function getImportService() | 21 | protected function getImportService() |
@@ -30,12 +38,4 @@ class WallabagV1Controller extends WallabagController | |||
30 | { | 38 | { |
31 | return 'WallabagImportBundle:WallabagV1:index.html.twig'; | 39 | return 'WallabagImportBundle:WallabagV1:index.html.twig'; |
32 | } | 40 | } |
33 | |||
34 | /** | ||
35 | * @Route("/wallabag-v1", name="import_wallabag_v1") | ||
36 | */ | ||
37 | public function indexAction(Request $request) | ||
38 | { | ||
39 | return parent::indexAction($request); | ||
40 | } | ||
41 | } | 41 | } |
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php b/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php index 45211fe6..ab26400c 100644 --- a/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php +++ b/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php | |||
@@ -8,6 +8,14 @@ use Symfony\Component\HttpFoundation\Request; | |||
8 | class WallabagV2Controller extends WallabagController | 8 | class WallabagV2Controller extends WallabagController |
9 | { | 9 | { |
10 | /** | 10 | /** |
11 | * @Route("/wallabag-v2", name="import_wallabag_v2") | ||
12 | */ | ||
13 | public function indexAction(Request $request) | ||
14 | { | ||
15 | return parent::indexAction($request); | ||
16 | } | ||
17 | |||
18 | /** | ||
11 | * {@inheritdoc} | 19 | * {@inheritdoc} |
12 | */ | 20 | */ |
13 | protected function getImportService() | 21 | protected function getImportService() |
@@ -30,12 +38,4 @@ class WallabagV2Controller extends WallabagController | |||
30 | { | 38 | { |
31 | return 'WallabagImportBundle:WallabagV2:index.html.twig'; | 39 | return 'WallabagImportBundle:WallabagV2:index.html.twig'; |
32 | } | 40 | } |
33 | |||
34 | /** | ||
35 | * @Route("/wallabag-v2", name="import_wallabag_v2") | ||
36 | */ | ||
37 | public function indexAction(Request $request) | ||
38 | { | ||
39 | return parent::indexAction($request); | ||
40 | } | ||
41 | } | 41 | } |
diff --git a/src/Wallabag/ImportBundle/DependencyInjection/WallabagImportExtension.php b/src/Wallabag/ImportBundle/DependencyInjection/WallabagImportExtension.php index 3f23c36b..cab70297 100644 --- a/src/Wallabag/ImportBundle/DependencyInjection/WallabagImportExtension.php +++ b/src/Wallabag/ImportBundle/DependencyInjection/WallabagImportExtension.php | |||
@@ -2,10 +2,10 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\DependencyInjection; | 3 | namespace Wallabag\ImportBundle\DependencyInjection; |
4 | 4 | ||
5 | use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
6 | use Symfony\Component\Config\FileLocator; | 5 | use Symfony\Component\Config\FileLocator; |
7 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; | 6 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
8 | use Symfony\Component\DependencyInjection\Loader; | 7 | use Symfony\Component\DependencyInjection\Loader; |
8 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; | ||
9 | 9 | ||
10 | class WallabagImportExtension extends Extension | 10 | class WallabagImportExtension extends Extension |
11 | { | 11 | { |
@@ -16,7 +16,7 @@ class WallabagImportExtension extends Extension | |||
16 | $container->setParameter('wallabag_import.allow_mimetypes', $config['allow_mimetypes']); | 16 | $container->setParameter('wallabag_import.allow_mimetypes', $config['allow_mimetypes']); |
17 | $container->setParameter('wallabag_import.resource_dir', $config['resource_dir']); | 17 | $container->setParameter('wallabag_import.resource_dir', $config['resource_dir']); |
18 | 18 | ||
19 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); | 19 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); |
20 | $loader->load('services.yml'); | 20 | $loader->load('services.yml'); |
21 | } | 21 | } |
22 | 22 | ||
diff --git a/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php b/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php index f50424c1..c50ef8c9 100644 --- a/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php +++ b/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php | |||
@@ -3,10 +3,10 @@ | |||
3 | namespace Wallabag\ImportBundle\Form\Type; | 3 | namespace Wallabag\ImportBundle\Form\Type; |
4 | 4 | ||
5 | use Symfony\Component\Form\AbstractType; | 5 | use Symfony\Component\Form\AbstractType; |
6 | use Symfony\Component\Form\FormBuilderInterface; | ||
7 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | ||
8 | use Symfony\Component\Form\Extension\Core\Type\FileType; | ||
9 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | 6 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; |
7 | use Symfony\Component\Form\Extension\Core\Type\FileType; | ||
8 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | ||
9 | use Symfony\Component\Form\FormBuilderInterface; | ||
10 | 10 | ||
11 | class UploadImportType extends AbstractType | 11 | class UploadImportType extends AbstractType |
12 | { | 12 | { |
diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php index 1d4a6e27..58a234f4 100644 --- a/src/Wallabag/ImportBundle/Import/AbstractImport.php +++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php | |||
@@ -2,35 +2,39 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Import; | 3 | namespace Wallabag\ImportBundle\Import; |
4 | 4 | ||
5 | use Doctrine\ORM\EntityManager; | ||
6 | use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface; | ||
5 | use Psr\Log\LoggerInterface; | 7 | use Psr\Log\LoggerInterface; |
6 | use Psr\Log\NullLogger; | 8 | use Psr\Log\NullLogger; |
7 | use Doctrine\ORM\EntityManager; | 9 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
8 | use Wallabag\CoreBundle\Helper\ContentProxy; | ||
9 | use Wallabag\CoreBundle\Entity\Entry; | 10 | use Wallabag\CoreBundle\Entity\Entry; |
10 | use Wallabag\CoreBundle\Entity\Tag; | 11 | use Wallabag\CoreBundle\Entity\Tag; |
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface; | ||
13 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||
14 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | 12 | use Wallabag\CoreBundle\Event\EntrySavedEvent; |
13 | use Wallabag\CoreBundle\Helper\ContentProxy; | ||
14 | use Wallabag\CoreBundle\Helper\TagsAssigner; | ||
15 | use Wallabag\UserBundle\Entity\User; | ||
15 | 16 | ||
16 | abstract class AbstractImport implements ImportInterface | 17 | abstract class AbstractImport implements ImportInterface |
17 | { | 18 | { |
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,55 @@ 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 | /** | ||
101 | * {@inheritdoc} | ||
102 | */ | ||
103 | public function getSummary() | ||
104 | { | ||
105 | return [ | ||
106 | 'skipped' => $this->skippedEntries, | ||
107 | 'imported' => $this->importedEntries, | ||
108 | 'queued' => $this->queuedEntries, | ||
109 | ]; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * Parse one entry. | ||
114 | * | ||
115 | * @param array $importedEntry | ||
116 | * | ||
117 | * @return Entry | ||
118 | */ | ||
119 | abstract public function parseEntry(array $importedEntry); | ||
120 | |||
121 | /** | ||
85 | * Fetch content from the ContentProxy (using graby). | 122 | * 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). | 123 | * If it fails return the given entry to be saved in all case (to avoid user to loose the content). |
87 | * | 124 | * |
88 | * @param Entry $entry Entry to update | 125 | * @param Entry $entry Entry to update |
89 | * @param string $url Url to grab content for | 126 | * @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 | 127 | * @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 | */ | 128 | */ |
94 | protected function fetchContent(Entry $entry, $url, array $content = []) | 129 | protected function fetchContent(Entry $entry, $url, array $content = []) |
95 | { | 130 | { |
96 | try { | 131 | try { |
97 | return $this->contentProxy->updateEntry($entry, $url, $content); | 132 | $this->contentProxy->updateEntry($entry, $url, $content, $this->disableContentUpdate); |
98 | } catch (\Exception $e) { | 133 | } catch (\Exception $e) { |
99 | return $entry; | 134 | $this->logger->error('Error trying to import an entry.', [ |
135 | 'entry_url' => $url, | ||
136 | 'error_msg' => $e->getMessage(), | ||
137 | ]); | ||
100 | } | 138 | } |
101 | } | 139 | } |
102 | 140 | ||
@@ -127,7 +165,7 @@ abstract class AbstractImport implements ImportInterface | |||
127 | $entryToBeFlushed[] = $entry; | 165 | $entryToBeFlushed[] = $entry; |
128 | 166 | ||
129 | // flush every 20 entries | 167 | // flush every 20 entries |
130 | if (($i % 20) === 0) { | 168 | if (0 === ($i % 20)) { |
131 | $this->em->flush(); | 169 | $this->em->flush(); |
132 | 170 | ||
133 | foreach ($entryToBeFlushed as $entry) { | 171 | foreach ($entryToBeFlushed as $entry) { |
@@ -179,27 +217,6 @@ abstract class AbstractImport implements ImportInterface | |||
179 | } | 217 | } |
180 | 218 | ||
181 | /** | 219 | /** |
182 | * {@inheritdoc} | ||
183 | */ | ||
184 | public function getSummary() | ||
185 | { | ||
186 | return [ | ||
187 | 'skipped' => $this->skippedEntries, | ||
188 | 'imported' => $this->importedEntries, | ||
189 | 'queued' => $this->queuedEntries, | ||
190 | ]; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * Parse one entry. | ||
195 | * | ||
196 | * @param array $importedEntry | ||
197 | * | ||
198 | * @return Entry | ||
199 | */ | ||
200 | abstract public function parseEntry(array $importedEntry); | ||
201 | |||
202 | /** | ||
203 | * Set current imported entry to archived / read. | 220 | * Set current imported entry to archived / read. |
204 | * Implementation is different accross all imports. | 221 | * Implementation is different accross all imports. |
205 | * | 222 | * |
diff --git a/src/Wallabag/ImportBundle/Import/BrowserImport.php b/src/Wallabag/ImportBundle/Import/BrowserImport.php index 8bf7d92e..b5593180 100644 --- a/src/Wallabag/ImportBundle/Import/BrowserImport.php +++ b/src/Wallabag/ImportBundle/Import/BrowserImport.php | |||
@@ -3,8 +3,6 @@ | |||
3 | namespace Wallabag\ImportBundle\Import; | 3 | namespace Wallabag\ImportBundle\Import; |
4 | 4 | ||
5 | use Wallabag\CoreBundle\Entity\Entry; | 5 | use Wallabag\CoreBundle\Entity\Entry; |
6 | use Wallabag\UserBundle\Entity\User; | ||
7 | use Wallabag\CoreBundle\Helper\ContentProxy; | ||
8 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | 6 | use Wallabag\CoreBundle\Event\EntrySavedEvent; |
9 | 7 | ||
10 | abstract class BrowserImport extends AbstractImport | 8 | abstract class BrowserImport extends AbstractImport |
@@ -75,6 +73,80 @@ abstract class BrowserImport extends AbstractImport | |||
75 | } | 73 | } |
76 | 74 | ||
77 | /** | 75 | /** |
76 | * {@inheritdoc} | ||
77 | */ | ||
78 | public function parseEntry(array $importedEntry) | ||
79 | { | ||
80 | if ((!array_key_exists('guid', $importedEntry) || (!array_key_exists('id', $importedEntry))) && is_array(reset($importedEntry))) { | ||
81 | if ($this->producer) { | ||
82 | $this->parseEntriesForProducer($importedEntry); | ||
83 | |||
84 | return; | ||
85 | } | ||
86 | |||
87 | $this->parseEntries($importedEntry); | ||
88 | |||
89 | return; | ||
90 | } | ||
91 | |||
92 | if (array_key_exists('children', $importedEntry)) { | ||
93 | if ($this->producer) { | ||
94 | $this->parseEntriesForProducer($importedEntry['children']); | ||
95 | |||
96 | return; | ||
97 | } | ||
98 | |||
99 | $this->parseEntries($importedEntry['children']); | ||
100 | |||
101 | return; | ||
102 | } | ||
103 | |||
104 | if (!array_key_exists('uri', $importedEntry) && !array_key_exists('url', $importedEntry)) { | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | $url = array_key_exists('uri', $importedEntry) ? $importedEntry['uri'] : $importedEntry['url']; | ||
109 | |||
110 | $existingEntry = $this->em | ||
111 | ->getRepository('WallabagCoreBundle:Entry') | ||
112 | ->findByUrlAndUserId($url, $this->user->getId()); | ||
113 | |||
114 | if (false !== $existingEntry) { | ||
115 | ++$this->skippedEntries; | ||
116 | |||
117 | return; | ||
118 | } | ||
119 | |||
120 | $data = $this->prepareEntry($importedEntry); | ||
121 | |||
122 | $entry = new Entry($this->user); | ||
123 | $entry->setUrl($data['url']); | ||
124 | $entry->setTitle($data['title']); | ||
125 | |||
126 | // update entry with content (in case fetching failed, the given entry will be return) | ||
127 | $this->fetchContent($entry, $data['url'], $data); | ||
128 | |||
129 | if (array_key_exists('tags', $data)) { | ||
130 | $this->tagsAssigner->assignTagsToEntry( | ||
131 | $entry, | ||
132 | $data['tags'] | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | $entry->setArchived($data['is_archived']); | ||
137 | |||
138 | if (!empty($data['created_at'])) { | ||
139 | $dt = new \DateTime(); | ||
140 | $entry->setCreatedAt($dt->setTimestamp($data['created_at'])); | ||
141 | } | ||
142 | |||
143 | $this->em->persist($entry); | ||
144 | ++$this->importedEntries; | ||
145 | |||
146 | return $entry; | ||
147 | } | ||
148 | |||
149 | /** | ||
78 | * Parse and insert all given entries. | 150 | * Parse and insert all given entries. |
79 | * | 151 | * |
80 | * @param $entries | 152 | * @param $entries |
@@ -99,7 +171,7 @@ abstract class BrowserImport extends AbstractImport | |||
99 | $entryToBeFlushed[] = $entry; | 171 | $entryToBeFlushed[] = $entry; |
100 | 172 | ||
101 | // flush every 20 entries | 173 | // flush every 20 entries |
102 | if (($i % 20) === 0) { | 174 | if (0 === ($i % 20)) { |
103 | $this->em->flush(); | 175 | $this->em->flush(); |
104 | 176 | ||
105 | foreach ($entryToBeFlushed as $entry) { | 177 | foreach ($entryToBeFlushed as $entry) { |
@@ -153,84 +225,12 @@ abstract class BrowserImport extends AbstractImport | |||
153 | /** | 225 | /** |
154 | * {@inheritdoc} | 226 | * {@inheritdoc} |
155 | */ | 227 | */ |
156 | public function parseEntry(array $importedEntry) | ||
157 | { | ||
158 | if ((!array_key_exists('guid', $importedEntry) || (!array_key_exists('id', $importedEntry))) && is_array(reset($importedEntry))) { | ||
159 | if ($this->producer) { | ||
160 | $this->parseEntriesForProducer($importedEntry); | ||
161 | |||
162 | return; | ||
163 | } | ||
164 | |||
165 | $this->parseEntries($importedEntry); | ||
166 | |||
167 | return; | ||
168 | } | ||
169 | |||
170 | if (array_key_exists('children', $importedEntry)) { | ||
171 | if ($this->producer) { | ||
172 | $this->parseEntriesForProducer($importedEntry['children']); | ||
173 | |||
174 | return; | ||
175 | } | ||
176 | |||
177 | $this->parseEntries($importedEntry['children']); | ||
178 | |||
179 | return; | ||
180 | } | ||
181 | |||
182 | if (!array_key_exists('uri', $importedEntry) && !array_key_exists('url', $importedEntry)) { | ||
183 | return; | ||
184 | } | ||
185 | |||
186 | $url = array_key_exists('uri', $importedEntry) ? $importedEntry['uri'] : $importedEntry['url']; | ||
187 | |||
188 | $existingEntry = $this->em | ||
189 | ->getRepository('WallabagCoreBundle:Entry') | ||
190 | ->findByUrlAndUserId($url, $this->user->getId()); | ||
191 | |||
192 | if (false !== $existingEntry) { | ||
193 | ++$this->skippedEntries; | ||
194 | |||
195 | return; | ||
196 | } | ||
197 | |||
198 | $data = $this->prepareEntry($importedEntry); | ||
199 | |||
200 | $entry = new Entry($this->user); | ||
201 | $entry->setUrl($data['url']); | ||
202 | $entry->setTitle($data['title']); | ||
203 | |||
204 | // update entry with content (in case fetching failed, the given entry will be return) | ||
205 | $entry = $this->fetchContent($entry, $data['url'], $data); | ||
206 | |||
207 | if (array_key_exists('tags', $data)) { | ||
208 | $this->contentProxy->assignTagsToEntry( | ||
209 | $entry, | ||
210 | $data['tags'] | ||
211 | ); | ||
212 | } | ||
213 | |||
214 | $entry->setArchived($data['is_archived']); | ||
215 | |||
216 | if (!empty($data['created_at'])) { | ||
217 | $dt = new \DateTime(); | ||
218 | $entry->setCreatedAt($dt->setTimestamp($data['created_at'])); | ||
219 | } | ||
220 | |||
221 | $this->em->persist($entry); | ||
222 | ++$this->importedEntries; | ||
223 | |||
224 | return $entry; | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * {@inheritdoc} | ||
229 | */ | ||
230 | protected function setEntryAsRead(array $importedEntry) | 228 | protected function setEntryAsRead(array $importedEntry) |
231 | { | 229 | { |
232 | $importedEntry['is_archived'] = 1; | 230 | $importedEntry['is_archived'] = 1; |
233 | 231 | ||
234 | return $importedEntry; | 232 | return $importedEntry; |
235 | } | 233 | } |
234 | |||
235 | abstract protected function prepareEntry(array $entry = []); | ||
236 | } | 236 | } |
diff --git a/src/Wallabag/ImportBundle/Import/ChromeImport.php b/src/Wallabag/ImportBundle/Import/ChromeImport.php index 2667890f..09183abe 100644 --- a/src/Wallabag/ImportBundle/Import/ChromeImport.php +++ b/src/Wallabag/ImportBundle/Import/ChromeImport.php | |||
@@ -45,7 +45,7 @@ class ChromeImport extends BrowserImport | |||
45 | 'created_at' => substr($entry['date_added'], 0, 10), | 45 | 'created_at' => substr($entry['date_added'], 0, 10), |
46 | ]; | 46 | ]; |
47 | 47 | ||
48 | if (array_key_exists('tags', $entry) && $entry['tags'] != '') { | 48 | if (array_key_exists('tags', $entry) && '' !== $entry['tags']) { |
49 | $data['tags'] = $entry['tags']; | 49 | $data['tags'] = $entry['tags']; |
50 | } | 50 | } |
51 | 51 | ||
diff --git a/src/Wallabag/ImportBundle/Import/FirefoxImport.php b/src/Wallabag/ImportBundle/Import/FirefoxImport.php index c50c69b3..73269fe1 100644 --- a/src/Wallabag/ImportBundle/Import/FirefoxImport.php +++ b/src/Wallabag/ImportBundle/Import/FirefoxImport.php | |||
@@ -45,7 +45,7 @@ class FirefoxImport extends BrowserImport | |||
45 | 'created_at' => substr($entry['dateAdded'], 0, 10), | 45 | 'created_at' => substr($entry['dateAdded'], 0, 10), |
46 | ]; | 46 | ]; |
47 | 47 | ||
48 | if (array_key_exists('tags', $entry) && $entry['tags'] != '') { | 48 | if (array_key_exists('tags', $entry) && '' !== $entry['tags']) { |
49 | $data['tags'] = $entry['tags']; | 49 | $data['tags'] = $entry['tags']; |
50 | } | 50 | } |
51 | 51 | ||
diff --git a/src/Wallabag/ImportBundle/Import/ImportCompilerPass.php b/src/Wallabag/ImportBundle/Import/ImportCompilerPass.php index a363a566..d7df0a83 100644 --- a/src/Wallabag/ImportBundle/Import/ImportCompilerPass.php +++ b/src/Wallabag/ImportBundle/Import/ImportCompilerPass.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle\Import; | 3 | namespace Wallabag\ImportBundle\Import; |
4 | 4 | ||
5 | use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
6 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | 5 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; |
6 | use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
7 | use Symfony\Component\DependencyInjection\Reference; | 7 | use Symfony\Component\DependencyInjection\Reference; |
8 | 8 | ||
9 | class ImportCompilerPass implements CompilerPassInterface | 9 | class ImportCompilerPass implements CompilerPassInterface |
diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php index 70a53f1a..7ab69e7a 100644 --- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php +++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php | |||
@@ -63,18 +63,27 @@ class InstapaperImport extends AbstractImport | |||
63 | 63 | ||
64 | $entries = []; | 64 | $entries = []; |
65 | $handle = fopen($this->filepath, 'r'); | 65 | $handle = fopen($this->filepath, 'r'); |
66 | while (($data = fgetcsv($handle, 10240)) !== false) { | 66 | while (false !== ($data = fgetcsv($handle, 10240))) { |
67 | if ('URL' === $data[0]) { | 67 | if ('URL' === $data[0]) { |
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'], true)) { | ||
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], |
74 | 'status' => $data[3], | 82 | 'status' => $data[3], |
75 | 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred', | 83 | 'is_archived' => 'Archive' === $data[3] || 'Starred' === $data[3], |
76 | 'is_starred' => $data[3] === 'Starred', | 84 | 'is_starred' => 'Starred' === $data[3], |
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..dddb87f4 100644 --- a/src/Wallabag/ImportBundle/Import/PocketImport.php +++ b/src/Wallabag/ImportBundle/Import/PocketImport.php | |||
@@ -5,15 +5,13 @@ 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 | { |
11 | const NB_ELEMENTS = 5000; | ||
12 | private $client; | 12 | private $client; |
13 | private $accessToken; | 13 | private $accessToken; |
14 | 14 | ||
15 | const NB_ELEMENTS = 5000; | ||
16 | |||
17 | /** | 15 | /** |
18 | * Only used for test purpose. | 16 | * Only used for test purpose. |
19 | * | 17 | * |
@@ -151,7 +149,7 @@ class PocketImport extends AbstractImport | |||
151 | // - first call get 5k offset 0 | 149 | // - first call get 5k offset 0 |
152 | // - second call get 5k offset 5k | 150 | // - second call get 5k offset 5k |
153 | // - and so on | 151 | // - and so on |
154 | if (count($entries['list']) === self::NB_ELEMENTS) { | 152 | if (self::NB_ELEMENTS === count($entries['list'])) { |
155 | ++$run; | 153 | ++$run; |
156 | 154 | ||
157 | return $this->import(self::NB_ELEMENTS * $run); | 155 | return $this->import(self::NB_ELEMENTS * $run); |
@@ -177,7 +175,7 @@ class PocketImport extends AbstractImport | |||
177 | */ | 175 | */ |
178 | public function parseEntry(array $importedEntry) | 176 | public function parseEntry(array $importedEntry) |
179 | { | 177 | { |
180 | $url = isset($importedEntry['resolved_url']) && $importedEntry['resolved_url'] != '' ? $importedEntry['resolved_url'] : $importedEntry['given_url']; | 178 | $url = isset($importedEntry['resolved_url']) && '' !== $importedEntry['resolved_url'] ? $importedEntry['resolved_url'] : $importedEntry['given_url']; |
181 | 179 | ||
182 | $existingEntry = $this->em | 180 | $existingEntry = $this->em |
183 | ->getRepository('WallabagCoreBundle:Entry') | 181 | ->getRepository('WallabagCoreBundle:Entry') |
@@ -193,18 +191,18 @@ class PocketImport extends AbstractImport | |||
193 | $entry->setUrl($url); | 191 | $entry->setUrl($url); |
194 | 192 | ||
195 | // update entry with content (in case fetching failed, the given entry will be return) | 193 | // update entry with content (in case fetching failed, the given entry will be return) |
196 | $entry = $this->fetchContent($entry, $url); | 194 | $this->fetchContent($entry, $url); |
197 | 195 | ||
198 | // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted | 196 | // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted |
199 | $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead); | 197 | $entry->setArchived(1 === $importedEntry['status'] || $this->markAsRead); |
200 | 198 | ||
201 | // 0 or 1 - 1 If the item is starred | 199 | // 0 or 1 - 1 If the item is starred |
202 | $entry->setStarred($importedEntry['favorite'] == 1); | 200 | $entry->setStarred(1 === $importedEntry['favorite']); |
203 | 201 | ||
204 | $title = 'Untitled'; | 202 | $title = 'Untitled'; |
205 | if (isset($importedEntry['resolved_title']) && $importedEntry['resolved_title'] != '') { | 203 | if (isset($importedEntry['resolved_title']) && '' !== $importedEntry['resolved_title']) { |
206 | $title = $importedEntry['resolved_title']; | 204 | $title = $importedEntry['resolved_title']; |
207 | } elseif (isset($importedEntry['given_title']) && $importedEntry['given_title'] != '') { | 205 | } elseif (isset($importedEntry['given_title']) && '' !== $importedEntry['given_title']) { |
208 | $title = $importedEntry['given_title']; | 206 | $title = $importedEntry['given_title']; |
209 | } | 207 | } |
210 | 208 | ||
@@ -216,7 +214,7 @@ class PocketImport extends AbstractImport | |||
216 | } | 214 | } |
217 | 215 | ||
218 | if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) { | 216 | if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) { |
219 | $this->contentProxy->assignTagsToEntry( | 217 | $this->tagsAssigner->assignTagsToEntry( |
220 | $entry, | 218 | $entry, |
221 | array_keys($importedEntry['tags']), | 219 | array_keys($importedEntry['tags']), |
222 | $this->em->getUnitOfWork()->getScheduledEntityInsertions() | 220 | $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..a35c411e 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,13 +54,14 @@ 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 |
47 | if (in_array($entry['title'], $this->untitled)) { | 58 | // If fetching fails again, they will get this instead of the v1 strings |
48 | $data['title'] = ''; | 59 | if (in_array($entry['title'], $this->untitled, true)) { |
49 | $data['html'] = ''; | 60 | $data['title'] = $this->fetchingErrorMessageTitle; |
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']) { |
53 | $data['tags'] = $entry['tags']; | 65 | $data['tags'] = $entry['tags']; |
54 | } | 66 | } |
55 | 67 | ||
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/Redis/Producer.php b/src/Wallabag/ImportBundle/Redis/Producer.php index fedc3e57..c77b5174 100644 --- a/src/Wallabag/ImportBundle/Redis/Producer.php +++ b/src/Wallabag/ImportBundle/Redis/Producer.php | |||
@@ -29,7 +29,7 @@ class Producer implements ProducerInterface | |||
29 | * @param string $routingKey NOT USED | 29 | * @param string $routingKey NOT USED |
30 | * @param array $additionalProperties NOT USED | 30 | * @param array $additionalProperties NOT USED |
31 | */ | 31 | */ |
32 | public function publish($msgBody, $routingKey = '', $additionalProperties = array()) | 32 | public function publish($msgBody, $routingKey = '', $additionalProperties = []) |
33 | { | 33 | { |
34 | $this->queue->sendJob($msgBody); | 34 | $this->queue->sendJob($msgBody); |
35 | } | 35 | } |
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/ImportBundle/WallabagImportBundle.php b/src/Wallabag/ImportBundle/WallabagImportBundle.php index a5ddc1b4..98c2f97b 100644 --- a/src/Wallabag/ImportBundle/WallabagImportBundle.php +++ b/src/Wallabag/ImportBundle/WallabagImportBundle.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\ImportBundle; | 3 | namespace Wallabag\ImportBundle; |
4 | 4 | ||
5 | use Symfony\Component\HttpKernel\Bundle\Bundle; | ||
6 | use Symfony\Component\DependencyInjection\ContainerBuilder; | 5 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
6 | use Symfony\Component\HttpKernel\Bundle\Bundle; | ||
7 | use Wallabag\ImportBundle\Import\ImportCompilerPass; | 7 | use Wallabag\ImportBundle\Import\ImportCompilerPass; |
8 | 8 | ||
9 | class WallabagImportBundle extends Bundle | 9 | class WallabagImportBundle extends Bundle |
diff --git a/src/Wallabag/UserBundle/Controller/ManageController.php b/src/Wallabag/UserBundle/Controller/ManageController.php index 92ee2b41..f3de656f 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 Symfony\Component\HttpFoundation\Request; | 7 | use Pagerfanta\Adapter\DoctrineORMAdapter; |
8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 8 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; |
9 | use Pagerfanta\Pagerfanta; | ||
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | 10 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; |
10 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 11 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
12 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
13 | use Symfony\Component\HttpFoundation\Request; | ||
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()) { |
@@ -64,13 +48,13 @@ class ManageController extends Controller | |||
64 | $this->get('translator')->trans('flashes.user.notice.added', ['%username%' => $user->getUsername()]) | 48 | $this->get('translator')->trans('flashes.user.notice.added', ['%username%' => $user->getUsername()]) |
65 | ); | 49 | ); |
66 | 50 | ||
67 | return $this->redirectToRoute('user_edit', array('id' => $user->getId())); | 51 | return $this->redirectToRoute('user_edit', ['id' => $user->getId()]); |
68 | } | 52 | } |
69 | 53 | ||
70 | return $this->render('WallabagUserBundle:Manage:new.html.twig', array( | 54 | return $this->render('WallabagUserBundle:Manage:new.html.twig', [ |
71 | 'user' => $user, | 55 | 'user' => $user, |
72 | 'form' => $form->createView(), | 56 | 'form' => $form->createView(), |
73 | )); | 57 | ]); |
74 | } | 58 | } |
75 | 59 | ||
76 | /** | 60 | /** |
@@ -95,15 +79,15 @@ class ManageController extends Controller | |||
95 | $this->get('translator')->trans('flashes.user.notice.updated', ['%username%' => $user->getUsername()]) | 79 | $this->get('translator')->trans('flashes.user.notice.updated', ['%username%' => $user->getUsername()]) |
96 | ); | 80 | ); |
97 | 81 | ||
98 | return $this->redirectToRoute('user_edit', array('id' => $user->getId())); | 82 | return $this->redirectToRoute('user_edit', ['id' => $user->getId()]); |
99 | } | 83 | } |
100 | 84 | ||
101 | return $this->render('WallabagUserBundle:Manage:edit.html.twig', array( | 85 | return $this->render('WallabagUserBundle:Manage:edit.html.twig', [ |
102 | 'user' => $user, | 86 | 'user' => $user, |
103 | 'edit_form' => $editForm->createView(), | 87 | 'edit_form' => $editForm->createView(), |
104 | 'delete_form' => $deleteForm->createView(), | 88 | 'delete_form' => $deleteForm->createView(), |
105 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), | 89 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), |
106 | )); | 90 | ]); |
107 | } | 91 | } |
108 | 92 | ||
109 | /** | 93 | /** |
@@ -132,6 +116,51 @@ class ManageController extends Controller | |||
132 | } | 116 | } |
133 | 117 | ||
134 | /** | 118 | /** |
119 | * @param Request $request | ||
120 | * @param int $page | ||
121 | * | ||
122 | * @Route("/list/{page}", name="user_index", defaults={"page" = 1}) | ||
123 | * | ||
124 | * Default parameter for page is hardcoded (in duplication of the defaults from the Route) | ||
125 | * because this controller is also called inside the layout template without any page as argument | ||
126 | * | ||
127 | * @return \Symfony\Component\HttpFoundation\Response | ||
128 | */ | ||
129 | public function searchFormAction(Request $request, $page = 1) | ||
130 | { | ||
131 | $em = $this->getDoctrine()->getManager(); | ||
132 | $qb = $em->getRepository('WallabagUserBundle:User')->createQueryBuilder('u'); | ||
133 | |||
134 | $form = $this->createForm(SearchUserType::class); | ||
135 | $form->handleRequest($request); | ||
136 | |||
137 | if ($form->isSubmitted() && $form->isValid()) { | ||
138 | $this->get('logger')->info('searching users'); | ||
139 | |||
140 | $searchTerm = (isset($request->get('search_user')['term']) ? $request->get('search_user')['term'] : ''); | ||
141 | |||
142 | $qb = $em->getRepository('WallabagUserBundle:User')->getQueryBuilderForSearch($searchTerm); | ||
143 | } | ||
144 | |||
145 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
146 | $pagerFanta = new Pagerfanta($pagerAdapter); | ||
147 | $pagerFanta->setMaxPerPage(50); | ||
148 | |||
149 | try { | ||
150 | $pagerFanta->setCurrentPage($page); | ||
151 | } catch (OutOfRangeCurrentPageException $e) { | ||
152 | if ($page > 1) { | ||
153 | return $this->redirect($this->generateUrl('user_index', ['page' => $pagerFanta->getNbPages()]), 302); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | return $this->render('WallabagUserBundle:Manage:index.html.twig', [ | ||
158 | 'searchForm' => $form->createView(), | ||
159 | 'users' => $pagerFanta, | ||
160 | ]); | ||
161 | } | ||
162 | |||
163 | /** | ||
135 | * Creates a form to delete a User entity. | 164 | * Creates a form to delete a User entity. |
136 | * | 165 | * |
137 | * @param User $user The User entity | 166 | * @param User $user The User entity |
@@ -141,7 +170,7 @@ class ManageController extends Controller | |||
141 | private function createDeleteForm(User $user) | 170 | private function createDeleteForm(User $user) |
142 | { | 171 | { |
143 | return $this->createFormBuilder() | 172 | return $this->createFormBuilder() |
144 | ->setAction($this->generateUrl('user_delete', array('id' => $user->getId()))) | 173 | ->setAction($this->generateUrl('user_delete', ['id' => $user->getId()])) |
145 | ->setMethod('DELETE') | 174 | ->setMethod('DELETE') |
146 | ->getForm() | 175 | ->getForm() |
147 | ; | 176 | ; |
diff --git a/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php b/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php index 99040f69..5ca3482e 100644 --- a/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php +++ b/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php | |||
@@ -2,10 +2,10 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\UserBundle\DependencyInjection; | 3 | namespace Wallabag\UserBundle\DependencyInjection; |
4 | 4 | ||
5 | use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
6 | use Symfony\Component\Config\FileLocator; | 5 | use Symfony\Component\Config\FileLocator; |
7 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; | 6 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
8 | use Symfony\Component\DependencyInjection\Loader; | 7 | use Symfony\Component\DependencyInjection\Loader; |
8 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; | ||
9 | 9 | ||
10 | class WallabagUserExtension extends Extension | 10 | class WallabagUserExtension extends Extension |
11 | { | 11 | { |
@@ -14,7 +14,7 @@ class WallabagUserExtension extends Extension | |||
14 | $configuration = new Configuration(); | 14 | $configuration = new Configuration(); |
15 | $config = $this->processConfiguration($configuration, $configs); | 15 | $config = $this->processConfiguration($configuration, $configs); |
16 | 16 | ||
17 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); | 17 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); |
18 | $loader->load('services.yml'); | 18 | $loader->load('services.yml'); |
19 | $container->setParameter('wallabag_user.registration_enabled', $config['registration_enabled']); | 19 | $container->setParameter('wallabag_user.registration_enabled', $config['registration_enabled']); |
20 | } | 20 | } |
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php index 3a167de7..48446e3c 100644 --- a/src/Wallabag/UserBundle/Entity/User.php +++ b/src/Wallabag/UserBundle/Entity/User.php | |||
@@ -4,37 +4,43 @@ 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 FOS\UserBundle\Model\User as BaseUser; | ||
8 | use JMS\Serializer\Annotation\Accessor; | ||
9 | use JMS\Serializer\Annotation\Groups; | ||
10 | use JMS\Serializer\Annotation\XmlRoot; | ||
7 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; | 11 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; |
8 | use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; | 12 | use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; |
9 | 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; |
15 | use Wallabag\CoreBundle\Entity\Config; | 16 | use Wallabag\CoreBundle\Entity\Config; |
16 | use Wallabag\CoreBundle\Entity\Entry; | 17 | use Wallabag\CoreBundle\Entity\Entry; |
18 | use Wallabag\CoreBundle\Helper\EntityTimestampsTrait; | ||
17 | 19 | ||
18 | /** | 20 | /** |
19 | * User. | 21 | * User. |
20 | * | 22 | * |
23 | * @XmlRoot("user") | ||
21 | * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") | 24 | * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") |
22 | * @ORM\Table(name="`user`") | 25 | * @ORM\Table(name="`user`") |
23 | * @ORM\HasLifecycleCallbacks() | 26 | * @ORM\HasLifecycleCallbacks() |
24 | * @ExclusionPolicy("all") | ||
25 | * | 27 | * |
26 | * @UniqueEntity("email") | 28 | * @UniqueEntity("email") |
27 | * @UniqueEntity("username") | 29 | * @UniqueEntity("username") |
28 | */ | 30 | */ |
29 | class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface | 31 | class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface |
30 | { | 32 | { |
33 | use EntityTimestampsTrait; | ||
34 | |||
35 | /** @Serializer\XmlAttribute */ | ||
31 | /** | 36 | /** |
32 | * @var int | 37 | * @var int |
33 | * | 38 | * |
34 | * @Expose | ||
35 | * @ORM\Column(name="id", type="integer") | 39 | * @ORM\Column(name="id", type="integer") |
36 | * @ORM\Id | 40 | * @ORM\Id |
37 | * @ORM\GeneratedValue(strategy="AUTO") | 41 | * @ORM\GeneratedValue(strategy="AUTO") |
42 | * | ||
43 | * @Groups({"user_api", "user_api_with_client"}) | ||
38 | */ | 44 | */ |
39 | protected $id; | 45 | protected $id; |
40 | 46 | ||
@@ -42,20 +48,40 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
42 | * @var string | 48 | * @var string |
43 | * | 49 | * |
44 | * @ORM\Column(name="name", type="text", nullable=true) | 50 | * @ORM\Column(name="name", type="text", nullable=true) |
51 | * | ||
52 | * @Groups({"user_api", "user_api_with_client"}) | ||
45 | */ | 53 | */ |
46 | protected $name; | 54 | protected $name; |
47 | 55 | ||
48 | /** | 56 | /** |
49 | * @var date | 57 | * @var string |
58 | * | ||
59 | * @Groups({"user_api", "user_api_with_client"}) | ||
60 | */ | ||
61 | protected $username; | ||
62 | |||
63 | /** | ||
64 | * @var string | ||
65 | * | ||
66 | * @Groups({"user_api", "user_api_with_client"}) | ||
67 | */ | ||
68 | protected $email; | ||
69 | |||
70 | /** | ||
71 | * @var \DateTime | ||
50 | * | 72 | * |
51 | * @ORM\Column(name="created_at", type="datetime") | 73 | * @ORM\Column(name="created_at", type="datetime") |
74 | * | ||
75 | * @Groups({"user_api", "user_api_with_client"}) | ||
52 | */ | 76 | */ |
53 | protected $createdAt; | 77 | protected $createdAt; |
54 | 78 | ||
55 | /** | 79 | /** |
56 | * @var date | 80 | * @var \DateTime |
57 | * | 81 | * |
58 | * @ORM\Column(name="updated_at", type="datetime") | 82 | * @ORM\Column(name="updated_at", type="datetime") |
83 | * | ||
84 | * @Groups({"user_api", "user_api_with_client"}) | ||
59 | */ | 85 | */ |
60 | protected $updatedAt; | 86 | protected $updatedAt; |
61 | 87 | ||
@@ -70,12 +96,35 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
70 | protected $config; | 96 | protected $config; |
71 | 97 | ||
72 | /** | 98 | /** |
99 | * @var ArrayCollection | ||
100 | * | ||
101 | * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\SiteCredential", mappedBy="user", cascade={"remove"}) | ||
102 | */ | ||
103 | protected $siteCredentials; | ||
104 | |||
105 | /** | ||
106 | * @var ArrayCollection | ||
107 | * | ||
108 | * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"}) | ||
109 | */ | ||
110 | protected $clients; | ||
111 | |||
112 | /** | ||
113 | * @see getFirstClient() below | ||
114 | * | ||
115 | * @Groups({"user_api_with_client"}) | ||
116 | * @Accessor(getter="getFirstClient") | ||
117 | */ | ||
118 | protected $default_client; | ||
119 | |||
120 | /** | ||
73 | * @ORM\Column(type="integer", nullable=true) | 121 | * @ORM\Column(type="integer", nullable=true) |
74 | */ | 122 | */ |
75 | private $authCode; | 123 | private $authCode; |
76 | 124 | ||
77 | /** | 125 | /** |
78 | * @var bool Enabled yes/no | 126 | * @var bool |
127 | * | ||
79 | * @ORM\Column(type="boolean") | 128 | * @ORM\Column(type="boolean") |
80 | */ | 129 | */ |
81 | private $twoFactorAuthentication = false; | 130 | private $twoFactorAuthentication = false; |
@@ -85,11 +134,6 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
85 | */ | 134 | */ |
86 | private $trusted; | 135 | private $trusted; |
87 | 136 | ||
88 | /** | ||
89 | * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"}) | ||
90 | */ | ||
91 | protected $clients; | ||
92 | |||
93 | public function __construct() | 137 | public function __construct() |
94 | { | 138 | { |
95 | parent::__construct(); | 139 | parent::__construct(); |
@@ -98,19 +142,6 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
98 | } | 142 | } |
99 | 143 | ||
100 | /** | 144 | /** |
101 | * @ORM\PrePersist | ||
102 | * @ORM\PreUpdate | ||
103 | */ | ||
104 | public function timestamps() | ||
105 | { | ||
106 | if (is_null($this->createdAt)) { | ||
107 | $this->createdAt = new \DateTime(); | ||
108 | } | ||
109 | |||
110 | $this->updatedAt = new \DateTime(); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Set name. | 145 | * Set name. |
115 | * | 146 | * |
116 | * @param string $name | 147 | * @param string $name |
@@ -135,7 +166,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
135 | } | 166 | } |
136 | 167 | ||
137 | /** | 168 | /** |
138 | * @return string | 169 | * @return \DateTime |
139 | */ | 170 | */ |
140 | public function getCreatedAt() | 171 | public function getCreatedAt() |
141 | { | 172 | { |
@@ -143,7 +174,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
143 | } | 174 | } |
144 | 175 | ||
145 | /** | 176 | /** |
146 | * @return string | 177 | * @return \DateTime |
147 | */ | 178 | */ |
148 | public function getUpdatedAt() | 179 | public function getUpdatedAt() |
149 | { | 180 | { |
@@ -266,4 +297,16 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf | |||
266 | { | 297 | { |
267 | return $this->clients; | 298 | return $this->clients; |
268 | } | 299 | } |
300 | |||
301 | /** | ||
302 | * 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). | ||
303 | * | ||
304 | * @return Client | ||
305 | */ | ||
306 | public function getFirstClient() | ||
307 | { | ||
308 | if (!empty($this->clients)) { | ||
309 | return $this->clients->first(); | ||
310 | } | ||
311 | } | ||
269 | } | 312 | } |
diff --git a/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php b/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php new file mode 100644 index 00000000..18f14a3a --- /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/EventListener/PasswordResettingListener.php b/src/Wallabag/UserBundle/EventListener/PasswordResettingListener.php index 3a7f2637..7df093f1 100644 --- a/src/Wallabag/UserBundle/EventListener/PasswordResettingListener.php +++ b/src/Wallabag/UserBundle/EventListener/PasswordResettingListener.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\UserBundle\EventListener; | 3 | namespace Wallabag\UserBundle\EventListener; |
4 | 4 | ||
5 | use FOS\UserBundle\FOSUserEvents; | ||
6 | use FOS\UserBundle\Event\FormEvent; | 5 | use FOS\UserBundle\Event\FormEvent; |
6 | use FOS\UserBundle\FOSUserEvents; | ||
7 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; | 7 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
8 | use Symfony\Component\HttpFoundation\RedirectResponse; | 8 | use Symfony\Component\HttpFoundation\RedirectResponse; |
9 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 9 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
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/Form/UserType.php b/src/Wallabag/UserBundle/Form/UserType.php index d8cdbaf9..56fea640 100644 --- a/src/Wallabag/UserBundle/Form/UserType.php +++ b/src/Wallabag/UserBundle/Form/UserType.php | |||
@@ -3,12 +3,12 @@ | |||
3 | namespace Wallabag\UserBundle\Form; | 3 | namespace Wallabag\UserBundle\Form; |
4 | 4 | ||
5 | use Symfony\Component\Form\AbstractType; | 5 | use Symfony\Component\Form\AbstractType; |
6 | use Symfony\Component\Form\FormBuilderInterface; | ||
7 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
8 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | 6 | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; |
7 | use Symfony\Component\Form\Extension\Core\Type\EmailType; | ||
9 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; | 8 | use Symfony\Component\Form\Extension\Core\Type\SubmitType; |
10 | use Symfony\Component\Form\Extension\Core\Type\TextType; | 9 | use Symfony\Component\Form\Extension\Core\Type\TextType; |
11 | use Symfony\Component\Form\Extension\Core\Type\EmailType; | 10 | use Symfony\Component\Form\FormBuilderInterface; |
11 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
12 | 12 | ||
13 | class UserType extends AbstractType | 13 | class UserType extends AbstractType |
14 | { | 14 | { |
@@ -50,8 +50,8 @@ class UserType extends AbstractType | |||
50 | */ | 50 | */ |
51 | public function configureOptions(OptionsResolver $resolver) | 51 | public function configureOptions(OptionsResolver $resolver) |
52 | { | 52 | { |
53 | $resolver->setDefaults(array( | 53 | $resolver->setDefaults([ |
54 | 'data_class' => 'Wallabag\UserBundle\Entity\User', | 54 | 'data_class' => 'Wallabag\UserBundle\Entity\User', |
55 | )); | 55 | ]); |
56 | } | 56 | } |
57 | } | 57 | } |
diff --git a/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php b/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php index 961208f2..aed805c9 100644 --- a/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php +++ b/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | namespace Wallabag\UserBundle\Mailer; | 3 | namespace Wallabag\UserBundle\Mailer; |
4 | 4 | ||
5 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; | ||
6 | use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface; | 5 | use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface; |
6 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; | ||
7 | 7 | ||
8 | /** | 8 | /** |
9 | * Custom mailer for TwoFactorBundle email. | 9 | * Custom mailer for TwoFactorBundle email. |
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index f913f52d..be693d3b 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php | |||
@@ -3,6 +3,8 @@ | |||
3 | namespace Wallabag\UserBundle\Repository; | 3 | namespace Wallabag\UserBundle\Repository; |
4 | 4 | ||
5 | use Doctrine\ORM\EntityRepository; | 5 | use Doctrine\ORM\EntityRepository; |
6 | use Doctrine\ORM\QueryBuilder; | ||
7 | use Wallabag\UserBundle\Entity\User; | ||
6 | 8 | ||
7 | class UserRepository extends EntityRepository | 9 | class UserRepository extends EntityRepository |
8 | { | 10 | { |
@@ -52,4 +54,30 @@ class UserRepository extends EntityRepository | |||
52 | ->getQuery() | 54 | ->getQuery() |
53 | ->getSingleScalarResult(); | 55 | ->getSingleScalarResult(); |
54 | } | 56 | } |
57 | |||
58 | /** | ||
59 | * Count how many users are existing. | ||
60 | * | ||
61 | * @return int | ||
62 | */ | ||
63 | public function getSumUsers() | ||
64 | { | ||
65 | return $this->createQueryBuilder('u') | ||
66 | ->select('count(u)') | ||
67 | ->getQuery() | ||
68 | ->getSingleScalarResult(); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Retrieves users filtered with a search term. | ||
73 | * | ||
74 | * @param string $term | ||
75 | * | ||
76 | * @return QueryBuilder | ||
77 | */ | ||
78 | public function getQueryBuilderForSearch($term) | ||
79 | { | ||
80 | return $this->createQueryBuilder('u') | ||
81 | ->andWhere('lower(u.username) LIKE lower(:term) OR lower(u.email) LIKE lower(:term) OR lower(u.name) LIKE lower(:term)')->setParameter('term', '%' . $term . '%'); | ||
82 | } | ||
55 | } | 83 | } |
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/translations/wallabag_user.oc.yml b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.oc.yml index 53a1afd1..e62ea2bc 100644 --- a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.oc.yml +++ b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.oc.yml | |||
@@ -5,7 +5,7 @@ auth_code: | |||
5 | subject: "Còdi d'autentificacion wallabag" | 5 | subject: "Còdi d'autentificacion wallabag" |
6 | body: | 6 | body: |
7 | hello: "Bonjorn %user%," | 7 | hello: "Bonjorn %user%," |
8 | first_para: "Estant qu'avètz activat la dobla autentificacion sus vòtre compte wallabag e que venètz de vos conectar dempuèi un novèl aparelh (ordinador, mobil, etc.) vos mandem un còdi per validar la connexion." | 8 | first_para: "Estant qu'avètz activat l'autentificacion en dos temps sus vòstre compte wallabag e que venètz de vos connectar dempuèi un novèl periferic (ordinador, mobil, etc.) vos mandem un còdi per validar la connexion." |
9 | second_para: "Vaquí lo còdi a dintrar :" | 9 | second_para: "Vaquí lo còdi per dintrar : " |
10 | support: "S'avètz un problèma de connexion, dobtetz pas a contacter l'assisténcia : " | 10 | support: "S'avètz un problèma de connexion, dobtetz pas a contactar l'assisténcia : " |
11 | signature: "La còla de wallabag" | 11 | signature: "La còla de wallabag" |
diff --git a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.ru.yml b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.ru.yml new file mode 100644 index 00000000..2a418224 --- /dev/null +++ b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.ru.yml | |||
@@ -0,0 +1,11 @@ | |||
1 | # Two factor mail | ||
2 | auth_code: | ||
3 | on: 'Вкл' | ||
4 | mailer: | ||
5 | subject: 'код аутентификации wallabag' | ||
6 | body: | ||
7 | hello: "Привет %user%," | ||
8 | first_para: "Поскольку вы активируете двухфакторную аутентификацию на своей учетной записи wallabag и только что вошли в систему с нового устройства (компьютер, телефон и т. д.), мы отправляем вам код для подтверждения вашего соединения." | ||
9 | second_para: "Вот код:" | ||
10 | support: "Пожалуйста, не стесняйтесь обращаться к нам, если у вас есть какие-либо проблемы:" | ||
11 | signature: "Команда wallabag" | ||
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> |