diff options
Diffstat (limited to 'src')
23 files changed, 586 insertions, 200 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 7590efbb..dbff6065 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -5,6 +5,7 @@ namespace Wallabag\ApiBundle\Controller; | |||
5 | use Hateoas\Configuration\Route; | 5 | use Hateoas\Configuration\Route; |
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | 6 | use Hateoas\Representation\Factory\PagerfantaFactory; |
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
8 | use Symfony\Component\HttpKernel\Exception\HttpException; | ||
8 | use Symfony\Component\HttpFoundation\Request; | 9 | use Symfony\Component\HttpFoundation\Request; |
9 | use Symfony\Component\HttpFoundation\JsonResponse; | 10 | use Symfony\Component\HttpFoundation\JsonResponse; |
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 11 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
@@ -44,9 +45,7 @@ class EntryRestController extends WallabagRestController | |||
44 | $results[$url] = $res instanceof Entry ? $res->getId() : false; | 45 | $results[$url] = $res instanceof Entry ? $res->getId() : false; |
45 | } | 46 | } |
46 | 47 | ||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | 48 | return $this->sendResponse($results); |
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | 49 | } |
51 | 50 | ||
52 | // let's see if it is a simple url? | 51 | // let's see if it is a simple url? |
@@ -62,9 +61,7 @@ class EntryRestController extends WallabagRestController | |||
62 | 61 | ||
63 | $exists = $res instanceof Entry ? $res->getId() : false; | 62 | $exists = $res instanceof Entry ? $res->getId() : false; |
64 | 63 | ||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | 64 | return $this->sendResponse(['exists' => $exists]); |
66 | |||
67 | return (new JsonResponse())->setJson($json); | ||
68 | } | 65 | } |
69 | 66 | ||
70 | /** | 67 | /** |
@@ -124,9 +121,7 @@ class EntryRestController extends WallabagRestController | |||
124 | ) | 121 | ) |
125 | ); | 122 | ); |
126 | 123 | ||
127 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | 124 | return $this->sendResponse($paginatedCollection); |
128 | |||
129 | return (new JsonResponse())->setJson($json); | ||
130 | } | 125 | } |
131 | 126 | ||
132 | /** | 127 | /** |
@@ -145,9 +140,7 @@ class EntryRestController extends WallabagRestController | |||
145 | $this->validateAuthentication(); | 140 | $this->validateAuthentication(); |
146 | $this->validateUserAccess($entry->getUser()->getId()); | 141 | $this->validateUserAccess($entry->getUser()->getId()); |
147 | 142 | ||
148 | $json = $this->get('serializer')->serialize($entry, 'json'); | 143 | return $this->sendResponse($entry); |
149 | |||
150 | return (new JsonResponse())->setJson($json); | ||
151 | } | 144 | } |
152 | 145 | ||
153 | /** | 146 | /** |
@@ -173,6 +166,110 @@ class EntryRestController extends WallabagRestController | |||
173 | } | 166 | } |
174 | 167 | ||
175 | /** | 168 | /** |
169 | * Handles an entries list and delete URL. | ||
170 | * | ||
171 | * @ApiDoc( | ||
172 | * parameters={ | ||
173 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} | ||
174 | * } | ||
175 | * ) | ||
176 | * | ||
177 | * @return JsonResponse | ||
178 | */ | ||
179 | public function deleteEntriesListAction(Request $request) | ||
180 | { | ||
181 | $this->validateAuthentication(); | ||
182 | |||
183 | $urls = json_decode($request->query->get('urls', [])); | ||
184 | |||
185 | if (empty($urls)) { | ||
186 | return $this->sendResponse([]); | ||
187 | } | ||
188 | |||
189 | $results = []; | ||
190 | |||
191 | // handle multiple urls | ||
192 | foreach ($urls as $key => $url) { | ||
193 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
194 | $url, | ||
195 | $this->getUser()->getId() | ||
196 | ); | ||
197 | |||
198 | $results[$key]['url'] = $url; | ||
199 | |||
200 | if (false !== $entry) { | ||
201 | $em = $this->getDoctrine()->getManager(); | ||
202 | $em->remove($entry); | ||
203 | $em->flush(); | ||
204 | |||
205 | // entry deleted, dispatch event about it! | ||
206 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
207 | } | ||
208 | |||
209 | $results[$key]['entry'] = $entry instanceof Entry ? true : false; | ||
210 | } | ||
211 | |||
212 | return $this->sendResponse($results); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Handles an entries list and create URL. | ||
217 | * | ||
218 | * @ApiDoc( | ||
219 | * parameters={ | ||
220 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} | ||
221 | * } | ||
222 | * ) | ||
223 | * | ||
224 | * @return JsonResponse | ||
225 | * | ||
226 | * @throws HttpException When limit is reached | ||
227 | */ | ||
228 | public function postEntriesListAction(Request $request) | ||
229 | { | ||
230 | $this->validateAuthentication(); | ||
231 | |||
232 | $urls = json_decode($request->query->get('urls', [])); | ||
233 | $results = []; | ||
234 | |||
235 | $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); | ||
236 | |||
237 | if (count($urls) > $limit) { | ||
238 | throw new HttpException(400, 'API limit reached'); | ||
239 | } | ||
240 | |||
241 | // handle multiple urls | ||
242 | if (!empty($urls)) { | ||
243 | foreach ($urls as $key => $url) { | ||
244 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
245 | $url, | ||
246 | $this->getUser()->getId() | ||
247 | ); | ||
248 | |||
249 | $results[$key]['url'] = $url; | ||
250 | |||
251 | if (false === $entry) { | ||
252 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
253 | new Entry($this->getUser()), | ||
254 | $url | ||
255 | ); | ||
256 | } | ||
257 | |||
258 | $em = $this->getDoctrine()->getManager(); | ||
259 | $em->persist($entry); | ||
260 | $em->flush(); | ||
261 | |||
262 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
263 | |||
264 | // entry saved, dispatch event about it! | ||
265 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | return $this->sendResponse($results); | ||
270 | } | ||
271 | |||
272 | /** | ||
176 | * Create an entry. | 273 | * Create an entry. |
177 | * | 274 | * |
178 | * @ApiDoc( | 275 | * @ApiDoc( |
@@ -229,9 +326,7 @@ class EntryRestController extends WallabagRestController | |||
229 | // entry saved, dispatch event about it! | 326 | // entry saved, dispatch event about it! |
230 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 327 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
231 | 328 | ||
232 | $json = $this->get('serializer')->serialize($entry, 'json'); | 329 | return $this->sendResponse($entry); |
233 | |||
234 | return (new JsonResponse())->setJson($json); | ||
235 | } | 330 | } |
236 | 331 | ||
237 | /** | 332 | /** |
@@ -280,9 +375,7 @@ class EntryRestController extends WallabagRestController | |||
280 | $em = $this->getDoctrine()->getManager(); | 375 | $em = $this->getDoctrine()->getManager(); |
281 | $em->flush(); | 376 | $em->flush(); |
282 | 377 | ||
283 | $json = $this->get('serializer')->serialize($entry, 'json'); | 378 | return $this->sendResponse($entry); |
284 | |||
285 | return (new JsonResponse())->setJson($json); | ||
286 | } | 379 | } |
287 | 380 | ||
288 | /** | 381 | /** |
@@ -325,9 +418,7 @@ class EntryRestController extends WallabagRestController | |||
325 | // entry saved, dispatch event about it! | 418 | // entry saved, dispatch event about it! |
326 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | 419 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
327 | 420 | ||
328 | $json = $this->get('serializer')->serialize($entry, 'json'); | 421 | return $this->sendResponse($entry); |
329 | |||
330 | return (new JsonResponse())->setJson($json); | ||
331 | } | 422 | } |
332 | 423 | ||
333 | /** | 424 | /** |
@@ -353,9 +444,7 @@ class EntryRestController extends WallabagRestController | |||
353 | // entry deleted, dispatch event about it! | 444 | // entry deleted, dispatch event about it! |
354 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | 445 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
355 | 446 | ||
356 | $json = $this->get('serializer')->serialize($entry, 'json'); | 447 | return $this->sendResponse($entry); |
357 | |||
358 | return (new JsonResponse())->setJson($json); | ||
359 | } | 448 | } |
360 | 449 | ||
361 | /** | 450 | /** |
@@ -374,9 +463,7 @@ class EntryRestController extends WallabagRestController | |||
374 | $this->validateAuthentication(); | 463 | $this->validateAuthentication(); |
375 | $this->validateUserAccess($entry->getUser()->getId()); | 464 | $this->validateUserAccess($entry->getUser()->getId()); |
376 | 465 | ||
377 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | 466 | return $this->sendResponse($entry->getTags()); |
378 | |||
379 | return (new JsonResponse())->setJson($json); | ||
380 | } | 467 | } |
381 | 468 | ||
382 | /** | 469 | /** |
@@ -407,9 +494,7 @@ class EntryRestController extends WallabagRestController | |||
407 | $em->persist($entry); | 494 | $em->persist($entry); |
408 | $em->flush(); | 495 | $em->flush(); |
409 | 496 | ||
410 | $json = $this->get('serializer')->serialize($entry, 'json'); | 497 | return $this->sendResponse($entry); |
411 | |||
412 | return (new JsonResponse())->setJson($json); | ||
413 | } | 498 | } |
414 | 499 | ||
415 | /** | 500 | /** |
@@ -434,9 +519,7 @@ class EntryRestController extends WallabagRestController | |||
434 | $em->persist($entry); | 519 | $em->persist($entry); |
435 | $em->flush(); | 520 | $em->flush(); |
436 | 521 | ||
437 | $json = $this->get('serializer')->serialize($entry, 'json'); | 522 | return $this->sendResponse($entry); |
438 | |||
439 | return (new JsonResponse())->setJson($json); | ||
440 | } | 523 | } |
441 | 524 | ||
442 | /** | 525 | /** |
@@ -455,45 +538,46 @@ class EntryRestController extends WallabagRestController | |||
455 | $this->validateAuthentication(); | 538 | $this->validateAuthentication(); |
456 | 539 | ||
457 | $list = json_decode($request->query->get('list', [])); | 540 | $list = json_decode($request->query->get('list', [])); |
458 | $results = []; | 541 | |
542 | if (empty($list)) { | ||
543 | return $this->sendResponse([]); | ||
544 | } | ||
459 | 545 | ||
460 | // handle multiple urls | 546 | // handle multiple urls |
461 | if (!empty($list)) { | 547 | $results = []; |
462 | foreach ($list as $key => $element) { | ||
463 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | ||
464 | $element->url, | ||
465 | $this->getUser()->getId() | ||
466 | ); | ||
467 | 548 | ||
468 | $results[$key]['url'] = $element->url; | 549 | foreach ($list as $key => $element) { |
469 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | 550 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
551 | $element->url, | ||
552 | $this->getUser()->getId() | ||
553 | ); | ||
470 | 554 | ||
471 | $tags = $element->tags; | 555 | $results[$key]['url'] = $element->url; |
556 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | ||
472 | 557 | ||
473 | if (false !== $entry && !(empty($tags))) { | 558 | $tags = $element->tags; |
474 | $tags = explode(',', $tags); | ||
475 | foreach ($tags as $label) { | ||
476 | $label = trim($label); | ||
477 | 559 | ||
478 | $tag = $this->getDoctrine() | 560 | if (false !== $entry && !(empty($tags))) { |
479 | ->getRepository('WallabagCoreBundle:Tag') | 561 | $tags = explode(',', $tags); |
480 | ->findOneByLabel($label); | 562 | foreach ($tags as $label) { |
563 | $label = trim($label); | ||
481 | 564 | ||
482 | if (false !== $tag) { | 565 | $tag = $this->getDoctrine() |
483 | $entry->removeTag($tag); | 566 | ->getRepository('WallabagCoreBundle:Tag') |
484 | } | 567 | ->findOneByLabel($label); |
485 | } | ||
486 | 568 | ||
487 | $em = $this->getDoctrine()->getManager(); | 569 | if (false !== $tag) { |
488 | $em->persist($entry); | 570 | $entry->removeTag($tag); |
489 | $em->flush(); | 571 | } |
490 | } | 572 | } |
573 | |||
574 | $em = $this->getDoctrine()->getManager(); | ||
575 | $em->persist($entry); | ||
576 | $em->flush(); | ||
491 | } | 577 | } |
492 | } | 578 | } |
493 | 579 | ||
494 | $json = $this->get('serializer')->serialize($results, 'json'); | 580 | return $this->sendResponse($results); |
495 | |||
496 | return (new JsonResponse())->setJson($json); | ||
497 | } | 581 | } |
498 | 582 | ||
499 | /** | 583 | /** |
@@ -512,32 +596,47 @@ class EntryRestController extends WallabagRestController | |||
512 | $this->validateAuthentication(); | 596 | $this->validateAuthentication(); |
513 | 597 | ||
514 | $list = json_decode($request->query->get('list', [])); | 598 | $list = json_decode($request->query->get('list', [])); |
599 | |||
600 | if (empty($list)) { | ||
601 | return $this->sendResponse([]); | ||
602 | } | ||
603 | |||
515 | $results = []; | 604 | $results = []; |
516 | 605 | ||
517 | // handle multiple urls | 606 | // handle multiple urls |
518 | if (!empty($list)) { | 607 | foreach ($list as $key => $element) { |
519 | foreach ($list as $key => $element) { | 608 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
520 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( | 609 | $element->url, |
521 | $element->url, | 610 | $this->getUser()->getId() |
522 | $this->getUser()->getId() | 611 | ); |
523 | ); | ||
524 | 612 | ||
525 | $results[$key]['url'] = $element->url; | 613 | $results[$key]['url'] = $element->url; |
526 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; | 614 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; |
527 | 615 | ||
528 | $tags = $element->tags; | 616 | $tags = $element->tags; |
529 | 617 | ||
530 | if (false !== $entry && !(empty($tags))) { | 618 | if (false !== $entry && !(empty($tags))) { |
531 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | 619 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); |
532 | 620 | ||
533 | $em = $this->getDoctrine()->getManager(); | 621 | $em = $this->getDoctrine()->getManager(); |
534 | $em->persist($entry); | 622 | $em->persist($entry); |
535 | $em->flush(); | 623 | $em->flush(); |
536 | } | ||
537 | } | 624 | } |
538 | } | 625 | } |
539 | 626 | ||
540 | $json = $this->get('serializer')->serialize($results, 'json'); | 627 | return $this->sendResponse($results); |
628 | } | ||
629 | |||
630 | /** | ||
631 | * Shortcut to send data serialized in json. | ||
632 | * | ||
633 | * @param mixed $data | ||
634 | * | ||
635 | * @return JsonResponse | ||
636 | */ | ||
637 | private function sendResponse($data) | ||
638 | { | ||
639 | $json = $this->get('serializer')->serialize($data, 'json'); | ||
541 | 640 | ||
542 | return (new JsonResponse())->setJson($json); | 641 | return (new JsonResponse())->setJson($json); |
543 | } | 642 | } |
diff --git a/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php new file mode 100644 index 00000000..65f35d8e --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php | |||
@@ -0,0 +1,119 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
7 | use Symfony\Component\Console\Input\InputArgument; | ||
8 | use Symfony\Component\Console\Input\InputInterface; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | use Wallabag\CoreBundle\Entity\Entry; | ||
11 | use Wallabag\UserBundle\Entity\User; | ||
12 | |||
13 | class CleanDuplicatesCommand extends ContainerAwareCommand | ||
14 | { | ||
15 | /** @var OutputInterface */ | ||
16 | protected $output; | ||
17 | |||
18 | protected $duplicates = 0; | ||
19 | |||
20 | protected function configure() | ||
21 | { | ||
22 | $this | ||
23 | ->setName('wallabag:clean-duplicates') | ||
24 | ->setDescription('Cleans the database for duplicates') | ||
25 | ->setHelp('This command helps you to clean your articles list in case of duplicates') | ||
26 | ->addArgument( | ||
27 | 'username', | ||
28 | InputArgument::OPTIONAL, | ||
29 | 'User to clean' | ||
30 | ); | ||
31 | } | ||
32 | |||
33 | protected function execute(InputInterface $input, OutputInterface $output) | ||
34 | { | ||
35 | $this->output = $output; | ||
36 | |||
37 | $username = $input->getArgument('username'); | ||
38 | |||
39 | if ($username) { | ||
40 | try { | ||
41 | $user = $this->getUser($username); | ||
42 | $this->cleanDuplicates($user); | ||
43 | } catch (NoResultException $e) { | ||
44 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $username)); | ||
45 | |||
46 | return 1; | ||
47 | } | ||
48 | } else { | ||
49 | $users = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findAll(); | ||
50 | |||
51 | $output->writeln(sprintf('Cleaning through %d user accounts', count($users))); | ||
52 | |||
53 | foreach ($users as $user) { | ||
54 | $output->writeln(sprintf('Processing user %s', $user->getUsername())); | ||
55 | $this->cleanDuplicates($user); | ||
56 | } | ||
57 | $output->writeln(sprintf('Finished cleaning. %d duplicates found in total', $this->duplicates)); | ||
58 | } | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * @param User $user | ||
65 | */ | ||
66 | private function cleanDuplicates(User $user) | ||
67 | { | ||
68 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | ||
69 | $repo = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); | ||
70 | |||
71 | $entries = $repo->getAllEntriesIdAndUrl($user->getId()); | ||
72 | |||
73 | $duplicatesCount = 0; | ||
74 | $urls = []; | ||
75 | foreach ($entries as $entry) { | ||
76 | $url = $this->similarUrl($entry['url']); | ||
77 | |||
78 | /* @var $entry Entry */ | ||
79 | if (in_array($url, $urls)) { | ||
80 | ++$duplicatesCount; | ||
81 | |||
82 | $em->remove($repo->find($entry['id'])); | ||
83 | $em->flush(); // Flushing at the end of the loop would require the instance not being online | ||
84 | } else { | ||
85 | $urls[] = $entry['url']; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | $this->duplicates += $duplicatesCount; | ||
90 | |||
91 | $this->output->writeln(sprintf('Cleaned %d duplicates for user %s', $duplicatesCount, $user->getUserName())); | ||
92 | } | ||
93 | |||
94 | private function similarUrl($url) | ||
95 | { | ||
96 | if (in_array(substr($url, -1), ['/', '#'])) { // get rid of "/" and "#" and the end of urls | ||
97 | return substr($url, 0, strlen($url)); | ||
98 | } | ||
99 | |||
100 | return $url; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * Fetches a user from its username. | ||
105 | * | ||
106 | * @param string $username | ||
107 | * | ||
108 | * @return \Wallabag\UserBundle\Entity\User | ||
109 | */ | ||
110 | private function getUser($username) | ||
111 | { | ||
112 | return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); | ||
113 | } | ||
114 | |||
115 | private function getDoctrine() | ||
116 | { | ||
117 | return $this->getContainer()->get('doctrine'); | ||
118 | } | ||
119 | } | ||
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php index 3c4d3f25..0d9364f6 100644 --- a/src/Wallabag/CoreBundle/Command/InstallCommand.php +++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php | |||
@@ -63,6 +63,7 @@ class InstallCommand extends ContainerAwareCommand | |||
63 | ->setupDatabase() | 63 | ->setupDatabase() |
64 | ->setupAdmin() | 64 | ->setupAdmin() |
65 | ->setupConfig() | 65 | ->setupConfig() |
66 | ->runMigrations() | ||
66 | ; | 67 | ; |
67 | 68 | ||
68 | $output->writeln('<info>wallabag has been successfully installed.</info>'); | 69 | $output->writeln('<info>wallabag has been successfully installed.</info>'); |
@@ -71,7 +72,7 @@ class InstallCommand extends ContainerAwareCommand | |||
71 | 72 | ||
72 | protected function checkRequirements() | 73 | protected function checkRequirements() |
73 | { | 74 | { |
74 | $this->defaultOutput->writeln('<info><comment>Step 1 of 4.</comment> Checking system requirements.</info>'); | 75 | $this->defaultOutput->writeln('<info><comment>Step 1 of 5.</comment> Checking system requirements.</info>'); |
75 | $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); | 76 | $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); |
76 | 77 | ||
77 | $rows = []; | 78 | $rows = []; |
@@ -175,11 +176,11 @@ class InstallCommand extends ContainerAwareCommand | |||
175 | 176 | ||
176 | protected function setupDatabase() | 177 | protected function setupDatabase() |
177 | { | 178 | { |
178 | $this->defaultOutput->writeln('<info><comment>Step 2 of 4.</comment> Setting up database.</info>'); | 179 | $this->defaultOutput->writeln('<info><comment>Step 2 of 5.</comment> Setting up database.</info>'); |
179 | 180 | ||
180 | // user want to reset everything? Don't care about what is already here | 181 | // user want to reset everything? Don't care about what is already here |
181 | if (true === $this->defaultInput->getOption('reset')) { | 182 | if (true === $this->defaultInput->getOption('reset')) { |
182 | $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache'); | 183 | $this->defaultOutput->writeln('Dropping database, creating database and schema, clearing the cache'); |
183 | 184 | ||
184 | $this | 185 | $this |
185 | ->runCommand('doctrine:database:drop', ['--force' => true]) | 186 | ->runCommand('doctrine:database:drop', ['--force' => true]) |
@@ -211,7 +212,7 @@ class InstallCommand extends ContainerAwareCommand | |||
211 | $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); | 212 | $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); |
212 | 213 | ||
213 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | 214 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { |
214 | $this->defaultOutput->writeln('Droping database, creating database and schema'); | 215 | $this->defaultOutput->writeln('Dropping database, creating database and schema'); |
215 | 216 | ||
216 | $this | 217 | $this |
217 | ->runCommand('doctrine:database:drop', ['--force' => true]) | 218 | ->runCommand('doctrine:database:drop', ['--force' => true]) |
@@ -221,7 +222,7 @@ class InstallCommand extends ContainerAwareCommand | |||
221 | } elseif ($this->isSchemaPresent()) { | 222 | } elseif ($this->isSchemaPresent()) { |
222 | $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); | 223 | $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); |
223 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { | 224 | if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { |
224 | $this->defaultOutput->writeln('Droping schema and creating schema'); | 225 | $this->defaultOutput->writeln('Dropping schema and creating schema'); |
225 | 226 | ||
226 | $this | 227 | $this |
227 | ->runCommand('doctrine:schema:drop', ['--force' => true]) | 228 | ->runCommand('doctrine:schema:drop', ['--force' => true]) |
@@ -246,7 +247,7 @@ class InstallCommand extends ContainerAwareCommand | |||
246 | 247 | ||
247 | protected function setupAdmin() | 248 | protected function setupAdmin() |
248 | { | 249 | { |
249 | $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>'); | 250 | $this->defaultOutput->writeln('<info><comment>Step 3 of 5.</comment> Administration setup.</info>'); |
250 | 251 | ||
251 | $questionHelper = $this->getHelperSet()->get('question'); | 252 | $questionHelper = $this->getHelperSet()->get('question'); |
252 | $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); | 253 | $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); |
@@ -285,7 +286,7 @@ class InstallCommand extends ContainerAwareCommand | |||
285 | 286 | ||
286 | protected function setupConfig() | 287 | protected function setupConfig() |
287 | { | 288 | { |
288 | $this->defaultOutput->writeln('<info><comment>Step 4 of 4.</comment> Config setup.</info>'); | 289 | $this->defaultOutput->writeln('<info><comment>Step 4 of 5.</comment> Config setup.</info>'); |
289 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | 290 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); |
290 | 291 | ||
291 | // cleanup before insert new stuff | 292 | // cleanup before insert new stuff |
@@ -464,6 +465,14 @@ class InstallCommand extends ContainerAwareCommand | |||
464 | return $this; | 465 | return $this; |
465 | } | 466 | } |
466 | 467 | ||
468 | protected function runMigrations() | ||
469 | { | ||
470 | $this->defaultOutput->writeln('<info><comment>Step 5 of 5.</comment> Run migrations.</info>'); | ||
471 | |||
472 | $this | ||
473 | ->runCommand('doctrine:migrations:migrate', ['--no-interaction' => true]); | ||
474 | } | ||
475 | |||
467 | /** | 476 | /** |
468 | * Run a command. | 477 | * Run a command. |
469 | * | 478 | * |
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php index 006a18c3..75b37729 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php | |||
@@ -47,6 +47,9 @@ class Configuration implements ConfigurationInterface | |||
47 | ->scalarNode('list_mode') | 47 | ->scalarNode('list_mode') |
48 | ->defaultValue(1) | 48 | ->defaultValue(1) |
49 | ->end() | 49 | ->end() |
50 | ->scalarNode('api_limit_mass_actions') | ||
51 | ->defaultValue(10) | ||
52 | ->end() | ||
50 | ->end() | 53 | ->end() |
51 | ; | 54 | ; |
52 | 55 | ||
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php index aa9ee339..c075c19f 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php | |||
@@ -26,6 +26,7 @@ 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.api_limit_mass_actions', $config['api_limit_mass_actions']); | ||
29 | 30 | ||
30 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); | 31 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
31 | $loader->load('services.yml'); | 32 | $loader->load('services.yml'); |
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index 1f22e901..6972e974 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php | |||
@@ -379,4 +379,34 @@ class EntryRepository extends EntityRepository | |||
379 | ->setParameter('userId', $userId) | 379 | ->setParameter('userId', $userId) |
380 | ->execute(); | 380 | ->execute(); |
381 | } | 381 | } |
382 | |||
383 | /** | ||
384 | * Get id and url from all entries | ||
385 | * Used for the clean-duplicates command. | ||
386 | */ | ||
387 | public function getAllEntriesIdAndUrl($userId) | ||
388 | { | ||
389 | $qb = $this->createQueryBuilder('e') | ||
390 | ->select('e.id, e.url') | ||
391 | ->where('e.user = :userid')->setParameter(':userid', $userId); | ||
392 | |||
393 | return $qb->getQuery()->getArrayResult(); | ||
394 | } | ||
395 | |||
396 | /** | ||
397 | * Find all entries by url and owner. | ||
398 | * | ||
399 | * @param $url | ||
400 | * @param $userId | ||
401 | * | ||
402 | * @return array | ||
403 | */ | ||
404 | public function findAllByUrlAndUserId($url, $userId) | ||
405 | { | ||
406 | return $this->createQueryBuilder('e') | ||
407 | ->where('e.url = :url')->setParameter('url', urldecode($url)) | ||
408 | ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) | ||
409 | ->getQuery() | ||
410 | ->getResult(); | ||
411 | } | ||
382 | } | 412 | } |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index e5211b57..57319af7 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml | |||
@@ -512,6 +512,8 @@ user: | |||
512 | # delete: Delete | 512 | # delete: Delete |
513 | # delete_confirm: Are you sure? | 513 | # delete_confirm: Are you sure? |
514 | # back_to_list: Back to list | 514 | # back_to_list: Back to list |
515 | search: | ||
516 | # placeholder: Filter by username or email | ||
515 | 517 | ||
516 | error: | 518 | error: |
517 | # page_title: An error occurred | 519 | # page_title: An error occurred |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml index 893a4564..a7bcecc6 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | delete: Löschen | 513 | delete: Löschen |
514 | delete_confirm: Bist du sicher? | 514 | delete_confirm: Bist du sicher? |
515 | back_to_list: Zurück zur Liste | 515 | back_to_list: Zurück zur Liste |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | page_title: Ein Fehler ist aufgetreten | 520 | page_title: Ein Fehler ist aufgetreten |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index 4b745683..1ef2874d 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | delete: Delete | 513 | delete: Delete |
514 | delete_confirm: Are you sure? | 514 | delete_confirm: Are you sure? |
515 | back_to_list: Back to list | 515 | back_to_list: Back to list |
516 | search: | ||
517 | placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | page_title: An error occurred | 520 | page_title: An error occurred |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml index 99d25859..6cd079b0 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | delete: Eliminar | 513 | delete: Eliminar |
514 | delete_confirm: ¿Estás seguro? | 514 | delete_confirm: ¿Estás seguro? |
515 | back_to_list: Volver a la lista | 515 | back_to_list: Volver a la lista |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | page_title: Ha ocurrido un error | 520 | page_title: Ha ocurrido un error |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml index ccd9d555..fb6e315e 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | # delete: Delete | 513 | # delete: Delete |
514 | # delete_confirm: Are you sure? | 514 | # delete_confirm: Are you sure? |
515 | # back_to_list: Back to list | 515 | # back_to_list: Back to list |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | # page_title: An error occurred | 520 | # page_title: An error occurred |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml index a0f100f7..ad886363 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml | |||
@@ -46,7 +46,7 @@ footer: | |||
46 | social: "Social" | 46 | social: "Social" |
47 | powered_by: "propulsé par" | 47 | powered_by: "propulsé par" |
48 | about: "À propos" | 48 | about: "À propos" |
49 | stats: Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! | 49 | stats: "Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour !" |
50 | 50 | ||
51 | config: | 51 | config: |
52 | page_title: "Configuration" | 52 | page_title: "Configuration" |
@@ -71,16 +71,16 @@ config: | |||
71 | 300_word: "Je lis environ 300 mots par minute" | 71 | 300_word: "Je lis environ 300 mots par minute" |
72 | 400_word: "Je lis environ 400 mots par minute" | 72 | 400_word: "Je lis environ 400 mots par minute" |
73 | action_mark_as_read: | 73 | action_mark_as_read: |
74 | label: 'Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?' | 74 | label: "Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?" |
75 | redirect_homepage: "À la page d'accueil" | 75 | redirect_homepage: "À la page d’accueil" |
76 | redirect_current_page: 'À la page courante' | 76 | redirect_current_page: "À la page courante" |
77 | pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données | 77 | pocket_consumer_key_label: "Clé d’authentification Pocket pour importer les données" |
78 | android_configuration: Configurez votre application Android | 78 | 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." | 79 | help_theme: "L’affichage de wallabag est personnalisable. C’est ici que vous choisissez le thème que vous préférez." |
80 | help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page." | 80 | help_items_per_page: "Vous pouvez définir le nombre d’articles affichés sur chaque page." |
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." | 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." |
82 | help_language: "Vous pouvez définir la langue de l'interface de wallabag." | 82 | 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." | 83 | help_pocket_consumer_key: "Nécessaire pour l’import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket." |
84 | form_rss: | 84 | 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." | 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." |
86 | token_label: "Jeton RSS" | 86 | token_label: "Jeton RSS" |
@@ -100,18 +100,18 @@ config: | |||
100 | twoFactorAuthentication_label: "Double authentification" | 100 | twoFactorAuthentication_label: "Double authentification" |
101 | help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." | 101 | help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." |
102 | delete: | 102 | delete: |
103 | title: Supprimer mon compte (attention danger !) | 103 | title: "Supprimer mon compte (attention danger !)" |
104 | description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté. | 104 | description: "Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c’est IRRÉVERSIBLE). Vous serez ensuite déconnecté." |
105 | confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE) | 105 | confirm: "Vous êtes vraiment sûr ? (C’EST IRRÉVERSIBLE)" |
106 | button: 'Supprimer mon compte' | 106 | button: "Supprimer mon compte" |
107 | reset: | 107 | reset: |
108 | title: Réinitialisation (attention danger !) | 108 | title: "Réinitialisation (attention danger !)" |
109 | description: En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES ! | 109 | description: "En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES !" |
110 | annotations: Supprimer TOUTES les annotations | 110 | annotations: "Supprimer TOUTES les annotations" |
111 | tags: Supprimer TOUS les tags | 111 | tags: "Supprimer TOUS les tags" |
112 | entries: Supprimer TOUS les articles | 112 | entries: "Supprimer TOUS les articles" |
113 | archived: Supprimer TOUS les articles archivés | 113 | archived: "Supprimer TOUS les articles archivés" |
114 | confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) | 114 | confirm: "Êtes-vous vraiment vraiment sûr ? (C’EST IRRÉVERSIBLE)" |
115 | form_password: | 115 | form_password: |
116 | description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." | 116 | description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." |
117 | old_password_label: "Mot de passe actuel" | 117 | old_password_label: "Mot de passe actuel" |
@@ -164,7 +164,7 @@ entry: | |||
164 | archived: "Articles lus" | 164 | archived: "Articles lus" |
165 | filtered: "Articles filtrés" | 165 | filtered: "Articles filtrés" |
166 | filtered_tags: "Articles filtrés par tags :" | 166 | filtered_tags: "Articles filtrés par tags :" |
167 | filtered_search: 'Articles filtrés par recherche :' | 167 | filtered_search: "Articles filtrés par recherche :" |
168 | untagged: "Article sans tag" | 168 | untagged: "Article sans tag" |
169 | list: | 169 | list: |
170 | 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." | 170 | 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." |
@@ -188,7 +188,7 @@ entry: | |||
188 | preview_picture_label: "A une photo" | 188 | preview_picture_label: "A une photo" |
189 | preview_picture_help: "Photo" | 189 | preview_picture_help: "Photo" |
190 | language_label: "Langue" | 190 | language_label: "Langue" |
191 | http_status_label: 'Statut HTTP' | 191 | http_status_label: "Statut HTTP" |
192 | reading_time: | 192 | reading_time: |
193 | label: "Durée de lecture en minutes" | 193 | label: "Durée de lecture en minutes" |
194 | from: "de" | 194 | from: "de" |
@@ -298,32 +298,32 @@ howto: | |||
298 | bookmarklet: | 298 | bookmarklet: |
299 | description: "Glissez et déposez ce lien dans votre barre de favoris :" | 299 | description: "Glissez et déposez ce lien dans votre barre de favoris :" |
300 | shortcuts: | 300 | shortcuts: |
301 | page_description: Voici les raccourcis disponibles dans wallabag. | 301 | page_description: "Voici les raccourcis disponibles dans wallabag." |
302 | shortcut: Raccourci | 302 | shortcut: "Raccourci" |
303 | action: Action | 303 | action: "Action" |
304 | all_pages_title: Raccourcis disponibles dans toutes les pages | 304 | all_pages_title: "Raccourcis disponibles dans toutes les pages" |
305 | go_unread: Afficher les articles non lus | 305 | go_unread: "Afficher les articles non lus" |
306 | go_starred: Afficher les articles favoris | 306 | go_starred: "Afficher les articles favoris" |
307 | go_archive: Afficher les articles lus | 307 | go_archive: "Afficher les articles lus" |
308 | go_all: Afficher tous les articles | 308 | go_all: "Afficher tous les articles" |
309 | go_tags: Afficher les tags | 309 | go_tags: "Afficher les tags" |
310 | go_config: Aller à la configuration | 310 | go_config: "Aller à la configuration" |
311 | go_import: Aller aux imports | 311 | go_import: "Aller aux imports" |
312 | go_developers: Aller à la section Développeurs | 312 | go_developers: "Aller à la section Développeurs" |
313 | go_howto: Afficher l'aide (cette page !) | 313 | go_howto: "Afficher l’aide (cette page !)" |
314 | go_logout: Se déconnecter | 314 | go_logout: "Se déconnecter" |
315 | list_title: Raccourcis disponibles dans les pages de liste | 315 | list_title: "Raccourcis disponibles dans les pages de liste" |
316 | search: Afficher le formulaire de recherche | 316 | search: "Afficher le formulaire de recherche" |
317 | article_title: Raccourcis disponibles quand on affiche un article | 317 | article_title: "Raccourcis disponibles quand on affiche un article" |
318 | open_original: Ouvrir l'URL originale de l'article | 318 | open_original: "Ouvrir l’URL originale de l’article" |
319 | toggle_favorite: Changer le statut Favori de l'article | 319 | toggle_favorite: "Changer le statut Favori de l’article" |
320 | toggle_archive: Changer le status Lu de l'article | 320 | toggle_archive: "Changer le status Lu de l’article" |
321 | delete: Supprimer l'article | 321 | delete: "Supprimer l’article" |
322 | material_title: Raccourcis disponibles avec le thème Material uniquement | 322 | material_title: "Raccourcis disponibles avec le thème Material uniquement" |
323 | add_link: Ajouter un nouvel article | 323 | add_link: "Ajouter un nouvel article" |
324 | hide_form: Masquer le formulaire courant (recherche ou nouvel article) | 324 | hide_form: "Masquer le formulaire courant (recherche ou nouvel article)" |
325 | arrows_navigation: Naviguer à travers les articles | 325 | arrows_navigation: "Naviguer à travers les articles" |
326 | open_article: Afficher l'article sélectionné | 326 | open_article: "Afficher l’article sélectionné" |
327 | 327 | ||
328 | quickstart: | 328 | quickstart: |
329 | page_title: "Pour bien débuter" | 329 | page_title: "Pour bien débuter" |
@@ -385,8 +385,8 @@ tag: | |||
385 | 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." | 385 | 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." |
386 | see_untagged_entries: "Voir les articles sans tag" | 386 | see_untagged_entries: "Voir les articles sans tag" |
387 | new: | 387 | new: |
388 | add: 'Ajouter' | 388 | add: "Ajouter" |
389 | placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.' | 389 | placeholder: "Vous pouvez ajouter plusieurs tags, séparés par une virgule." |
390 | 390 | ||
391 | import: | 391 | import: |
392 | page_title: "Importer" | 392 | page_title: "Importer" |
@@ -420,7 +420,7 @@ import: | |||
420 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." | 420 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." |
421 | worker: | 421 | worker: |
422 | enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" | 422 | enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" |
423 | 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." | 423 | 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." |
424 | firefox: | 424 | firefox: |
425 | page_title: "Import > Firefox" | 425 | page_title: "Import > Firefox" |
426 | 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>" | 426 | 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>" |
@@ -489,16 +489,16 @@ developer: | |||
489 | back: "Retour" | 489 | back: "Retour" |
490 | 490 | ||
491 | user: | 491 | user: |
492 | page_title: Gestion des utilisateurs | 492 | page_title: "Gestion des utilisateurs" |
493 | new_user: Créer un nouvel utilisateur | 493 | new_user: "Créer un nouvel utilisateur" |
494 | edit_user: Éditer un utilisateur existant | 494 | edit_user: "Éditer un utilisateur existant" |
495 | description: Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression) | 495 | description: "Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression)" |
496 | list: | 496 | list: |
497 | actions: Actions | 497 | actions: "Actions" |
498 | edit_action: Éditer | 498 | edit_action: "Éditer" |
499 | yes: Oui | 499 | yes: "Oui" |
500 | no: Non | 500 | no: "Non" |
501 | create_new_one: Créer un nouvel utilisateur | 501 | create_new_one: "Créer un nouvel utilisateur" |
502 | form: | 502 | form: |
503 | username_label: "Nom d’utilisateur" | 503 | username_label: "Nom d’utilisateur" |
504 | name_label: "Nom" | 504 | name_label: "Nom" |
@@ -513,9 +513,11 @@ user: | |||
513 | delete: "Supprimer" | 513 | delete: "Supprimer" |
514 | delete_confirm: "Voulez-vous vraiment ?" | 514 | delete_confirm: "Voulez-vous vraiment ?" |
515 | back_to_list: "Revenir à la liste" | 515 | back_to_list: "Revenir à la liste" |
516 | search: | ||
517 | placeholder: "Filtrer par nom d’utilisateur ou email" | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | page_title: Une erreur est survenue | 520 | page_title: "Une erreur est survenue" |
519 | 521 | ||
520 | flashes: | 522 | flashes: |
521 | config: | 523 | config: |
@@ -528,10 +530,10 @@ flashes: | |||
528 | tagging_rules_updated: "Règles mises à jour" | 530 | tagging_rules_updated: "Règles mises à jour" |
529 | tagging_rules_deleted: "Règle supprimée" | 531 | tagging_rules_deleted: "Règle supprimée" |
530 | rss_token_updated: "Jeton RSS mis à jour" | 532 | rss_token_updated: "Jeton RSS mis à jour" |
531 | annotations_reset: Annotations supprimées | 533 | annotations_reset: "Annotations supprimées" |
532 | tags_reset: Tags supprimés | 534 | tags_reset: "Tags supprimés" |
533 | entries_reset: Articles supprimés | 535 | entries_reset: "Articles supprimés" |
534 | archived_reset: Articles archivés supprimés | 536 | archived_reset: "Articles archivés supprimés" |
535 | entry: | 537 | entry: |
536 | notice: | 538 | notice: |
537 | entry_already_saved: "Article déjà sauvegardé le %date%" | 539 | entry_already_saved: "Article déjà sauvegardé le %date%" |
@@ -563,6 +565,6 @@ flashes: | |||
563 | client_deleted: "Client %name% supprimé" | 565 | client_deleted: "Client %name% supprimé" |
564 | user: | 566 | user: |
565 | notice: | 567 | notice: |
566 | added: 'Utilisateur "%username%" ajouté' | 568 | added: "Utilisateur \"%username%\" ajouté" |
567 | updated: 'Utilisateur "%username%" mis à jour' | 569 | updated: "Utilisateur \"%username%\" mis à jour" |
568 | deleted: 'Utilisateur "%username%" supprimé' | 570 | deleted: "Utilisateur \"%username%\" supprimé" |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml index 374071ce..5a9605ff 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | # delete: Delete | 513 | # delete: Delete |
514 | # delete_confirm: Are you sure? | 514 | # delete_confirm: Are you sure? |
515 | # back_to_list: Back to list | 515 | # back_to_list: Back to list |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | # page_title: An error occurred | 520 | # page_title: An error occurred |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml index b01c611b..942bc257 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | delete: 'Suprimir' | 513 | delete: 'Suprimir' |
514 | delete_confirm: 'Sètz segur ?' | 514 | delete_confirm: 'Sètz segur ?' |
515 | back_to_list: 'Tornar a la lista' | 515 | back_to_list: 'Tornar a la lista' |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | page_title: Una error s'es produsida | 520 | page_title: Una error s'es produsida |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index d76ac328..fea90440 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | delete: Usuń | 513 | delete: Usuń |
514 | delete_confirm: JesteÅ› pewien? | 514 | delete_confirm: JesteÅ› pewien? |
515 | back_to_list: Powrót do listy | 515 | back_to_list: Powrót do listy |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | page_title: Wystąpił błąd | 520 | page_title: Wystąpił błąd |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml index 98dfcd25..c59991f8 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | delete: 'Apagar' | 513 | delete: 'Apagar' |
514 | delete_confirm: 'Tem certeza?' | 514 | delete_confirm: 'Tem certeza?' |
515 | back_to_list: 'Voltar para a lista' | 515 | back_to_list: 'Voltar para a lista' |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | # page_title: An error occurred | 520 | # page_title: An error occurred |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml index 8c07c13f..5846b7cc 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | # delete: Delete | 513 | # delete: Delete |
514 | # delete_confirm: Are you sure? | 514 | # delete_confirm: Are you sure? |
515 | # back_to_list: Back to list | 515 | # back_to_list: Back to list |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | # page_title: An error occurred | 520 | # page_title: An error occurred |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index bd21cb67..430fb96b 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml | |||
@@ -513,6 +513,8 @@ user: | |||
513 | # delete: Delete | 513 | # delete: Delete |
514 | # delete_confirm: Are you sure? | 514 | # delete_confirm: Are you sure? |
515 | # back_to_list: Back to list | 515 | # back_to_list: Back to list |
516 | search: | ||
517 | # placeholder: Filter by username or email | ||
516 | 518 | ||
517 | error: | 519 | error: |
518 | # page_title: An error occurred | 520 | # page_title: An error occurred |
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php index 28d01715..ce72837a 100644 --- a/src/Wallabag/ImportBundle/Command/ImportCommand.php +++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php | |||
@@ -15,10 +15,11 @@ class ImportCommand extends ContainerAwareCommand | |||
15 | $this | 15 | $this |
16 | ->setName('wallabag:import') | 16 | ->setName('wallabag:import') |
17 | ->setDescription('Import entries from a JSON export') | 17 | ->setDescription('Import entries from a JSON export') |
18 | ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate') | 18 | ->addArgument('username', InputArgument::REQUIRED, 'User to populate') |
19 | ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') | 19 | ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') |
20 | ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1') | 20 | ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1') |
21 | ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false) | 21 | ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false) |
22 | ->addOption('useUserId', null, InputArgument::OPTIONAL, 'Use user id instead of username to find account', false) | ||
22 | ; | 23 | ; |
23 | } | 24 | } |
24 | 25 | ||
@@ -34,10 +35,14 @@ class ImportCommand extends ContainerAwareCommand | |||
34 | // Turning off doctrine default logs queries for saving memory | 35 | // Turning off doctrine default logs queries for saving memory |
35 | $em->getConnection()->getConfiguration()->setSQLLogger(null); | 36 | $em->getConnection()->getConfiguration()->setSQLLogger(null); |
36 | 37 | ||
37 | $user = $em->getRepository('WallabagUserBundle:User')->findOneById($input->getArgument('userId')); | 38 | if ($input->getOption('useUserId')) { |
39 | $user = $em->getRepository('WallabagUserBundle:User')->findOneById($input->getArgument('username')); | ||
40 | } else { | ||
41 | $user = $em->getRepository('WallabagUserBundle:User')->findOneByUsername($input->getArgument('username')); | ||
42 | } | ||
38 | 43 | ||
39 | if (!is_object($user)) { | 44 | if (!is_object($user)) { |
40 | throw new Exception(sprintf('User with id "%s" not found', $input->getArgument('userId'))); | 45 | throw new Exception(sprintf('User "%s" not found', $input->getArgument('username'))); |
41 | } | 46 | } |
42 | 47 | ||
43 | switch ($input->getOption('importer')) { | 48 | switch ($input->getOption('importer')) { |
diff --git a/src/Wallabag/UserBundle/Controller/ManageController.php b/src/Wallabag/UserBundle/Controller/ManageController.php index 92ee2b41..1c5c86d4 100644 --- a/src/Wallabag/UserBundle/Controller/ManageController.php +++ b/src/Wallabag/UserBundle/Controller/ManageController.php | |||
@@ -4,12 +4,15 @@ namespace Wallabag\UserBundle\Controller; | |||
4 | 4 | ||
5 | use FOS\UserBundle\Event\UserEvent; | 5 | use FOS\UserBundle\Event\UserEvent; |
6 | use FOS\UserBundle\FOSUserEvents; | 6 | use FOS\UserBundle\FOSUserEvents; |
7 | use Pagerfanta\Adapter\DoctrineORMAdapter; | ||
8 | use Pagerfanta\Exception\OutOfRangeCurrentPageException; | ||
9 | use Pagerfanta\Pagerfanta; | ||
7 | use Symfony\Component\HttpFoundation\Request; | 10 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 11 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | 12 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; |
10 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 13 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
11 | use Wallabag\UserBundle\Entity\User; | 14 | use Wallabag\UserBundle\Entity\User; |
12 | use Wallabag\CoreBundle\Entity\Config; | 15 | use Wallabag\UserBundle\Form\SearchUserType; |
13 | 16 | ||
14 | /** | 17 | /** |
15 | * User controller. | 18 | * User controller. |
@@ -17,23 +20,6 @@ use Wallabag\CoreBundle\Entity\Config; | |||
17 | class ManageController extends Controller | 20 | class ManageController extends Controller |
18 | { | 21 | { |
19 | /** | 22 | /** |
20 | * Lists all User entities. | ||
21 | * | ||
22 | * @Route("/", name="user_index") | ||
23 | * @Method("GET") | ||
24 | */ | ||
25 | public function indexAction() | ||
26 | { | ||
27 | $em = $this->getDoctrine()->getManager(); | ||
28 | |||
29 | $users = $em->getRepository('WallabagUserBundle:User')->findAll(); | ||
30 | |||
31 | return $this->render('WallabagUserBundle:Manage:index.html.twig', array( | ||
32 | 'users' => $users, | ||
33 | )); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Creates a new User entity. | 23 | * Creates a new User entity. |
38 | * | 24 | * |
39 | * @Route("/new", name="user_new") | 25 | * @Route("/new", name="user_new") |
@@ -146,4 +132,49 @@ class ManageController extends Controller | |||
146 | ->getForm() | 132 | ->getForm() |
147 | ; | 133 | ; |
148 | } | 134 | } |
135 | |||
136 | /** | ||
137 | * @param Request $request | ||
138 | * @param int $page | ||
139 | * | ||
140 | * @Route("/list/{page}", name="user_index", defaults={"page" = 1}) | ||
141 | * | ||
142 | * Default parameter for page is hardcoded (in duplication of the defaults from the Route) | ||
143 | * because this controller is also called inside the layout template without any page as argument | ||
144 | * | ||
145 | * @return \Symfony\Component\HttpFoundation\Response | ||
146 | */ | ||
147 | public function searchFormAction(Request $request, $page = 1) | ||
148 | { | ||
149 | $em = $this->getDoctrine()->getManager(); | ||
150 | $qb = $em->getRepository('WallabagUserBundle:User')->createQueryBuilder('u'); | ||
151 | |||
152 | $form = $this->createForm(SearchUserType::class); | ||
153 | $form->handleRequest($request); | ||
154 | |||
155 | if ($form->isSubmitted() && $form->isValid()) { | ||
156 | $this->get('logger')->info('searching users'); | ||
157 | |||
158 | $searchTerm = (isset($request->get('search_user')['term']) ? $request->get('search_user')['term'] : ''); | ||
159 | |||
160 | $qb = $em->getRepository('WallabagUserBundle:User')->getQueryBuilderForSearch($searchTerm); | ||
161 | } | ||
162 | |||
163 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
164 | $pagerFanta = new Pagerfanta($pagerAdapter); | ||
165 | $pagerFanta->setMaxPerPage(50); | ||
166 | |||
167 | try { | ||
168 | $pagerFanta->setCurrentPage($page); | ||
169 | } catch (OutOfRangeCurrentPageException $e) { | ||
170 | if ($page > 1) { | ||
171 | return $this->redirect($this->generateUrl('user_index', ['page' => $pagerFanta->getNbPages()]), 302); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | return $this->render('WallabagUserBundle:Manage:index.html.twig', [ | ||
176 | 'searchForm' => $form->createView(), | ||
177 | 'users' => $pagerFanta, | ||
178 | ]); | ||
179 | } | ||
149 | } | 180 | } |
diff --git a/src/Wallabag/UserBundle/Form/SearchUserType.php b/src/Wallabag/UserBundle/Form/SearchUserType.php new file mode 100644 index 00000000..9ce46ee1 --- /dev/null +++ b/src/Wallabag/UserBundle/Form/SearchUserType.php | |||
@@ -0,0 +1,29 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\UserBundle\Form; | ||
4 | |||
5 | use Symfony\Component\Form\AbstractType; | ||
6 | use Symfony\Component\Form\Extension\Core\Type\TextType; | ||
7 | use Symfony\Component\Form\FormBuilderInterface; | ||
8 | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
9 | |||
10 | class SearchUserType extends AbstractType | ||
11 | { | ||
12 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
13 | { | ||
14 | $builder | ||
15 | ->setMethod('GET') | ||
16 | ->add('term', TextType::class, [ | ||
17 | 'required' => true, | ||
18 | 'label' => 'user.new.form_search.term_label', | ||
19 | ]) | ||
20 | ; | ||
21 | } | ||
22 | |||
23 | public function configureOptions(OptionsResolver $resolver) | ||
24 | { | ||
25 | $resolver->setDefaults([ | ||
26 | 'csrf_protection' => false, | ||
27 | ]); | ||
28 | } | ||
29 | } | ||
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index f913f52d..6adbe329 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php | |||
@@ -52,4 +52,17 @@ class UserRepository extends EntityRepository | |||
52 | ->getQuery() | 52 | ->getQuery() |
53 | ->getSingleScalarResult(); | 53 | ->getSingleScalarResult(); |
54 | } | 54 | } |
55 | |||
56 | /** | ||
57 | * Retrieves users filtered with a search term. | ||
58 | * | ||
59 | * @param string $term | ||
60 | * | ||
61 | * @return QueryBuilder | ||
62 | */ | ||
63 | public function getQueryBuilderForSearch($term) | ||
64 | { | ||
65 | return $this->createQueryBuilder('u') | ||
66 | ->andWhere('lower(u.username) LIKE lower(:term) OR lower(u.email) LIKE lower(:term) OR lower(u.name) LIKE lower(:term)')->setParameter('term', '%'.$term.'%'); | ||
67 | } | ||
55 | } | 68 | } |
diff --git a/src/Wallabag/UserBundle/Resources/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> |