]> git.immae.eu Git - github/wallabag/wallabag.git/blame - src/Wallabag/CoreBundle/Controller/EntryController.php
Merge remote-tracking branch 'origin/master' into 2.2
[github/wallabag/wallabag.git] / src / Wallabag / CoreBundle / Controller / EntryController.php
CommitLineData
9d50517c
NL
1<?php
2
ad4d1caa 3namespace Wallabag\CoreBundle\Controller;
9d50517c 4
619cc453 5use Pagerfanta\Adapter\DoctrineORMAdapter;
671a2b88 6use Pagerfanta\Exception\OutOfRangeCurrentPageException;
9d50517c
NL
7use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
8use Symfony\Bundle\FrameworkBundle\Controller\Controller;
163eae0b 9use Symfony\Component\HttpFoundation\Request;
2863bf2a 10use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
be463487 11use Wallabag\CoreBundle\Entity\Entry;
b0b893ea 12use Wallabag\CoreBundle\Form\Type\EntryFilterType;
619cc453
JB
13use Wallabag\CoreBundle\Form\Type\EditEntryType;
14use Wallabag\CoreBundle\Form\Type\NewEntryType;
222e09f1 15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
e0597476
JB
16use Wallabag\CoreBundle\Event\EntrySavedEvent;
17use Wallabag\CoreBundle\Event\EntryDeletedEvent;
9d50517c
NL
18
19class EntryController extends Controller
20{
880a0e1c 21 /**
59b97fae
JB
22 * Fetch content and update entry.
23 * In case it fails, entry will return to avod loosing the data.
24 *
25 * @param Entry $entry
26 * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded
27 *
28 * @return Entry
880a0e1c 29 */
59b97fae 30 private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved')
880a0e1c 31 {
59b97fae
JB
32 // put default title in case of fetching content failed
33 $entry->setTitle('No title found');
34
35 $message = 'flashes.entry.notice.'.$prefixMessage;
36
880a0e1c
NL
37 try {
38 $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
880a0e1c 39 } catch (\Exception $e) {
39ba51ca
JB
40 $this->get('logger')->error('Error while saving an entry', [
41 'exception' => $e,
42 'entry' => $entry,
43 ]);
44
59b97fae 45 $message = 'flashes.entry.notice.'.$prefixMessage.'_failed';
880a0e1c
NL
46 }
47
59b97fae
JB
48 $this->get('session')->getFlashBag()->add('notice', $message);
49
50 return $entry;
880a0e1c
NL
51 }
52
b84a8055 53 /**
3d2b2d62
J
54 * @param Request $request
55 *
053b9568 56 * @Route("/new-entry", name="new_entry")
3d2b2d62 57 *
b84a8055
NL
58 * @return \Symfony\Component\HttpFoundation\Response
59 */
053b9568 60 public function addEntryFormAction(Request $request)
b84a8055 61 {
3b815d2d 62 $entry = new Entry($this->getUser());
b84a8055 63
5c895a7f 64 $form = $this->createForm(NewEntryType::class, $entry);
b84a8055
NL
65
66 $form->handleRequest($request);
67
68 if ($form->isValid()) {
b00a89e0 69 $existingEntry = $this->checkIfEntryAlreadyExists($entry);
dda57bb9 70
5a4bbcc9 71 if (false !== $existingEntry) {
dda57bb9
NL
72 $this->get('session')->getFlashBag()->add(
73 'notice',
4094ea47 74 $this->get('translator')->trans('flashes.entry.notice.entry_already_saved', ['%date%' => $existingEntry->getCreatedAt()->format('d-m-Y')])
dda57bb9
NL
75 );
76
4094ea47 77 return $this->redirect($this->generateUrl('view', ['id' => $existingEntry->getId()]));
dda57bb9
NL
78 }
79
59b97fae 80 $this->updateEntry($entry);
39ba51ca 81
59b97fae
JB
82 $em = $this->getDoctrine()->getManager();
83 $em->persist($entry);
84 $em->flush();
b84a8055 85
e0597476
JB
86 // entry saved, dispatch event about it!
87 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
88
b84a8055
NL
89 return $this->redirect($this->generateUrl('homepage'));
90 }
91
4094ea47 92 return $this->render('WallabagCoreBundle:Entry:new_form.html.twig', [
b84a8055 93 'form' => $form->createView(),
4094ea47 94 ]);
b84a8055
NL
95 }
96
880a0e1c
NL
97 /**
98 * @param Request $request
99 *
100 * @Route("/bookmarklet", name="bookmarklet")
101 *
102 * @return \Symfony\Component\HttpFoundation\Response
103 */
5f8a7857 104 public function addEntryViaBookmarkletAction(Request $request)
880a0e1c
NL
105 {
106 $entry = new Entry($this->getUser());
107 $entry->setUrl($request->get('url'));
f652f41d 108
b00a89e0 109 if (false === $this->checkIfEntryAlreadyExists($entry)) {
f652f41d 110 $this->updateEntry($entry);
59b97fae
JB
111
112 $em = $this->getDoctrine()->getManager();
113 $em->persist($entry);
114 $em->flush();
e0597476
JB
115
116 // entry saved, dispatch event about it!
117 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
f652f41d 118 }
880a0e1c
NL
119
120 return $this->redirect($this->generateUrl('homepage'));
121 }
122
053b9568 123 /**
053b9568
NL
124 * @Route("/new", name="new")
125 *
126 * @return \Symfony\Component\HttpFoundation\Response
127 */
4d0ec0e7 128 public function addEntryAction()
053b9568
NL
129 {
130 return $this->render('WallabagCoreBundle:Entry:new.html.twig');
131 }
132
82d6d9cb
JB
133 /**
134 * Edit an entry content.
135 *
136 * @param Request $request
137 * @param Entry $entry
138 *
139 * @Route("/edit/{id}", requirements={"id" = "\d+"}, name="edit")
140 *
141 * @return \Symfony\Component\HttpFoundation\Response
142 */
143 public function editEntryAction(Request $request, Entry $entry)
144 {
145 $this->checkUserAction($entry);
146
5c895a7f 147 $form = $this->createForm(EditEntryType::class, $entry);
82d6d9cb
JB
148
149 $form->handleRequest($request);
150
151 if ($form->isValid()) {
152 $em = $this->getDoctrine()->getManager();
153 $em->persist($entry);
154 $em->flush();
155
156 $this->get('session')->getFlashBag()->add(
157 'notice',
4204a06b 158 'flashes.entry.notice.entry_updated'
82d6d9cb
JB
159 );
160
4094ea47 161 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
82d6d9cb
JB
162 }
163
4094ea47 164 return $this->render('WallabagCoreBundle:Entry:edit.html.twig', [
82d6d9cb 165 'form' => $form->createView(),
4094ea47 166 ]);
82d6d9cb
JB
167 }
168
89659c9e
NL
169 /**
170 * Shows all entries for current user.
171 *
172 * @param Request $request
173 * @param int $page
174 *
175 * @Route("/all/list/{page}", name="all", defaults={"page" = "1"})
176 *
177 * @return \Symfony\Component\HttpFoundation\Response
178 */
179 public function showAllAction(Request $request, $page)
180 {
2b7a4889 181 return $this->showEntries('all', $request, $page);
89659c9e
NL
182 }
183
9d50517c 184 /**
4346a860 185 * Shows unread entries for current user.
163eae0b 186 *
26864574
NL
187 * @param Request $request
188 * @param int $page
189 *
9fb6ac83 190 * @Route("/unread/list/{page}", name="unread", defaults={"page" = "1"})
3d2b2d62 191 *
163eae0b 192 * @return \Symfony\Component\HttpFoundation\Response
9d50517c 193 */
26864574 194 public function showUnreadAction(Request $request, $page)
9d50517c 195 {
5c072d2b
NL
196 // load the quickstart if no entry in database
197 if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUsername($this->getUser()->getId()) == 0) {
198 return $this->redirect($this->generateUrl('quickstart'));
199 }
200
0ab7404f 201 return $this->showEntries('unread', $request, $page);
9d50517c 202 }
bd9f0815
NL
203
204 /**
4346a860 205 * Shows read entries for current user.
163eae0b 206 *
26864574
NL
207 * @param Request $request
208 * @param int $page
209 *
9fb6ac83 210 * @Route("/archive/list/{page}", name="archive", defaults={"page" = "1"})
3d2b2d62 211 *
163eae0b 212 * @return \Symfony\Component\HttpFoundation\Response
bd9f0815 213 */
26864574 214 public function showArchiveAction(Request $request, $page)
bd9f0815 215 {
0ab7404f
JB
216 return $this->showEntries('archive', $request, $page);
217 }
218
219 /**
220 * Shows starred entries for current user.
221 *
222 * @param Request $request
223 * @param int $page
224 *
225 * @Route("/starred/list/{page}", name="starred", defaults={"page" = "1"})
226 *
227 * @return \Symfony\Component\HttpFoundation\Response
228 */
229 public function showStarredAction(Request $request, $page)
230 {
231 return $this->showEntries('starred', $request, $page);
232 }
26864574 233
0ab7404f
JB
234 /**
235 * Global method to retrieve entries depending on the given type
236 * It returns the response to be send.
237 *
238 * @param string $type Entries type: unread, starred or archive
239 * @param Request $request
240 * @param int $page
241 *
242 * @return \Symfony\Component\HttpFoundation\Response
243 */
244 private function showEntries($type, Request $request, $page)
245 {
5c072d2b 246 $repository = $this->get('wallabag_core.entry_repository');
0ab7404f
JB
247
248 switch ($type) {
b6520f0b
NL
249 case 'untagged':
250 $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId());
251
252 break;
0ab7404f
JB
253 case 'starred':
254 $qb = $repository->getBuilderForStarredByUser($this->getUser()->getId());
255 break;
256
257 case 'archive':
258 $qb = $repository->getBuilderForArchiveByUser($this->getUser()->getId());
259 break;
260
261 case 'unread':
262 $qb = $repository->getBuilderForUnreadByUser($this->getUser()->getId());
263 break;
264
2b7a4889
NL
265 case 'all':
266 $qb = $repository->getBuilderForAllByUser($this->getUser()->getId());
267 break;
268
0ab7404f
JB
269 default:
270 throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
271 }
272
1d76102a 273 $form = $this->createForm(EntryFilterType::class);
9fb6ac83 274
26864574
NL
275 if ($request->query->has($form->getName())) {
276 // manually bind values from the request
277 $form->submit($request->query->get($form->getName()));
278
279 // build the query from the given form object
0ab7404f 280 $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $qb);
26864574
NL
281 }
282
0ab7404f 283 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
26864574 284
891456ba
NL
285 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')
286 ->prepare($pagerAdapter, $page);
287
671a2b88
ML
288 try {
289 $entries->setCurrentPage($page);
290 } catch (OutOfRangeCurrentPageException $e) {
291 if ($page > 1) {
4094ea47 292 return $this->redirect($this->generateUrl($type, ['page' => $entries->getNbPages()]), 302);
671a2b88
ML
293 }
294 }
bd9f0815
NL
295
296 return $this->render(
ad4d1caa 297 'WallabagCoreBundle:Entry:entries.html.twig',
4094ea47 298 [
8ce32af6
JB
299 'form' => $form->createView(),
300 'entries' => $entries,
301 'currentPage' => $page,
4094ea47 302 ]
bd9f0815 303 );
bd9f0815
NL
304 }
305
306 /**
4346a860 307 * Shows entry content.
163eae0b 308 *
3d2b2d62
J
309 * @param Entry $entry
310 *
bd9f0815 311 * @Route("/view/{id}", requirements={"id" = "\d+"}, name="view")
3d2b2d62 312 *
163eae0b 313 * @return \Symfony\Component\HttpFoundation\Response
bd9f0815 314 */
be463487 315 public function viewAction(Entry $entry)
bd9f0815 316 {
3d2b2d62
J
317 $this->checkUserAction($entry);
318
bd9f0815 319 return $this->render(
ad4d1caa 320 'WallabagCoreBundle:Entry:entry.html.twig',
4094ea47 321 ['entry' => $entry]
bd9f0815 322 );
163eae0b
NL
323 }
324
831b02aa
JB
325 /**
326 * Reload an entry.
327 * Refetch content from the website and make it readable again.
328 *
329 * @param Entry $entry
330 *
331 * @Route("/reload/{id}", requirements={"id" = "\d+"}, name="reload_entry")
332 *
333 * @return \Symfony\Component\HttpFoundation\RedirectResponse
334 */
335 public function reloadAction(Entry $entry)
336 {
337 $this->checkUserAction($entry);
338
59b97fae 339 $this->updateEntry($entry, 'entry_reloaded');
831b02aa 340
2297d60f
JB
341 // if refreshing entry failed, don't save it
342 if ($this->getParameter('wallabag_core.fetching_error_message') === $entry->getContent()) {
343 $bag = $this->get('session')->getFlashBag();
344 $bag->clear();
345 $bag->add('notice', 'flashes.entry.notice.entry_reloaded_failed');
346
347 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
348 }
349
59b97fae
JB
350 $em = $this->getDoctrine()->getManager();
351 $em->persist($entry);
352 $em->flush();
831b02aa 353
e0597476
JB
354 // entry saved, dispatch event about it!
355 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
356
4094ea47 357 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
831b02aa
JB
358 }
359
163eae0b 360 /**
4346a860 361 * Changes read status for an entry.
163eae0b 362 *
3d2b2d62
J
363 * @param Request $request
364 * @param Entry $entry
365 *
163eae0b 366 * @Route("/archive/{id}", requirements={"id" = "\d+"}, name="archive_entry")
3d2b2d62 367 *
163eae0b
NL
368 * @return \Symfony\Component\HttpFoundation\RedirectResponse
369 */
be463487 370 public function toggleArchiveAction(Request $request, Entry $entry)
163eae0b 371 {
3d2b2d62
J
372 $this->checkUserAction($entry);
373
163eae0b
NL
374 $entry->toggleArchive();
375 $this->getDoctrine()->getManager()->flush();
376
4204a06b
JB
377 $message = 'flashes.entry.notice.entry_unarchived';
378 if ($entry->isArchived()) {
379 $message = 'flashes.entry.notice.entry_archived';
380 }
381
163eae0b
NL
382 $this->get('session')->getFlashBag()->add(
383 'notice',
4204a06b 384 $message
163eae0b
NL
385 );
386
af497a64
NL
387 $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer'));
388
389 return $this->redirect($redirectUrl);
163eae0b
NL
390 }
391
392 /**
eecd7e40 393 * Changes starred status for an entry.
163eae0b 394 *
3d2b2d62
J
395 * @param Request $request
396 * @param Entry $entry
397 *
163eae0b 398 * @Route("/star/{id}", requirements={"id" = "\d+"}, name="star_entry")
3d2b2d62 399 *
163eae0b
NL
400 * @return \Symfony\Component\HttpFoundation\RedirectResponse
401 */
be463487 402 public function toggleStarAction(Request $request, Entry $entry)
163eae0b 403 {
3d2b2d62
J
404 $this->checkUserAction($entry);
405
163eae0b
NL
406 $entry->toggleStar();
407 $this->getDoctrine()->getManager()->flush();
408
4204a06b
JB
409 $message = 'flashes.entry.notice.entry_unstarred';
410 if ($entry->isStarred()) {
411 $message = 'flashes.entry.notice.entry_starred';
412 }
413
163eae0b
NL
414 $this->get('session')->getFlashBag()->add(
415 'notice',
4204a06b 416 $message
163eae0b
NL
417 );
418
af497a64
NL
419 $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer'));
420
421 return $this->redirect($redirectUrl);
163eae0b
NL
422 }
423
424 /**
2863bf2a 425 * Deletes entry and redirect to the homepage or the last viewed page.
163eae0b 426 *
16a3d04c 427 * @param Entry $entry
3d2b2d62 428 *
163eae0b 429 * @Route("/delete/{id}", requirements={"id" = "\d+"}, name="delete_entry")
3d2b2d62 430 *
163eae0b
NL
431 * @return \Symfony\Component\HttpFoundation\RedirectResponse
432 */
18d5f454 433 public function deleteEntryAction(Request $request, Entry $entry)
163eae0b 434 {
3d2b2d62
J
435 $this->checkUserAction($entry);
436
2863bf2a
JB
437 // generates the view url for this entry to check for redirection later
438 // to avoid redirecting to the deleted entry. Ugh.
439 $url = $this->generateUrl(
440 'view',
4094ea47 441 ['id' => $entry->getId()],
ce0e9ec3 442 UrlGeneratorInterface::ABSOLUTE_PATH
2863bf2a
JB
443 );
444
e0597476
JB
445 // entry deleted, dispatch event about it!
446 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
447
1d147791
NL
448 $em = $this->getDoctrine()->getManager();
449 $em->remove($entry);
450 $em->flush();
163eae0b
NL
451
452 $this->get('session')->getFlashBag()->add(
453 'notice',
4204a06b 454 'flashes.entry.notice.entry_deleted'
163eae0b 455 );
bd9f0815 456
ce0e9ec3
JB
457 // don't redirect user to the deleted entry (check that the referer doesn't end with the same url)
458 $referer = $request->headers->get('referer');
459 $to = (1 !== preg_match('#'.$url.'$#i', $referer) ? $referer : null);
af497a64
NL
460
461 $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($to);
462
463 return $this->redirect($redirectUrl);
bd9f0815 464 }
3d2b2d62
J
465
466 /**
4346a860 467 * Check if the logged user can manage the given entry.
3d2b2d62
J
468 *
469 * @param Entry $entry
470 */
471 private function checkUserAction(Entry $entry)
472 {
f1be7af4 473 if (null === $this->getUser() || $this->getUser()->getId() != $entry->getUser()->getId()) {
82d6d9cb 474 throw $this->createAccessDeniedException('You can not access this entry.');
3d2b2d62
J
475 }
476 }
b00a89e0
NL
477
478 /**
479 * Check for existing entry, if it exists, redirect to it with a message.
480 *
4094ea47 481 * @param Entry $entry
b00a89e0 482 *
4094ea47 483 * @return Entry|bool
b00a89e0 484 */
4094ea47 485 private function checkIfEntryAlreadyExists(Entry $entry)
b00a89e0
NL
486 {
487 return $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($entry->getUrl(), $this->getUser()->getId());
f3d0cb91
NL
488 }
489
f1be7af4
NL
490 /**
491 * Get public URL for entry (and generate it if necessary).
492 *
493 * @param Entry $entry
494 *
495 * @Route("/share/{id}", requirements={"id" = "\d+"}, name="share")
496 *
497 * @return \Symfony\Component\HttpFoundation\Response
498 */
499 public function shareAction(Entry $entry)
500 {
501 $this->checkUserAction($entry);
502
eddda878
JB
503 if (null === $entry->getUuid()) {
504 $entry->generateUuid();
505
506 $em = $this->getDoctrine()->getManager();
507 $em->persist($entry);
508 $em->flush();
f1be7af4
NL
509 }
510
511 return $this->redirect($this->generateUrl('share_entry', [
512 'uuid' => $entry->getUuid(),
513 ]));
514 }
515
516 /**
517 * Disable public sharing for an entry.
518 *
519 * @param Entry $entry
520 *
521 * @Route("/share/delete/{id}", requirements={"id" = "\d+"}, name="delete_share")
522 *
523 * @return \Symfony\Component\HttpFoundation\Response
524 */
525 public function deleteShareAction(Entry $entry)
526 {
527 $this->checkUserAction($entry);
528
529 $entry->cleanUuid();
eddda878 530
f1be7af4
NL
531 $em = $this->getDoctrine()->getManager();
532 $em->persist($entry);
533 $em->flush();
534
535 return $this->redirect($this->generateUrl('view', [
536 'id' => $entry->getId(),
537 ]));
538 }
539
d0545b6b 540 /**
eddda878 541 * Ability to view a content publicly.
f3d0cb91
NL
542 *
543 * @param Entry $entry
544 *
f1be7af4 545 * @Route("/share/{uuid}", requirements={"uuid" = ".+"}, name="share_entry")
eddda878 546 * @Cache(maxage="25200", smaxage="25200", public=true)
f3d0cb91
NL
547 *
548 * @return \Symfony\Component\HttpFoundation\Response
549 */
d0545b6b 550 public function shareEntryAction(Entry $entry)
f3d0cb91 551 {
eddda878
JB
552 if (!$this->get('craue_config')->get('share_public')) {
553 throw $this->createAccessDeniedException('Sharing an entry is disabled for this user.');
554 }
555
f3d0cb91 556 return $this->render(
2ff9991a 557 '@WallabagCore/themes/common/Entry/share.html.twig',
eddda878 558 ['entry' => $entry]
f3d0cb91
NL
559 );
560 }
b6520f0b
NL
561
562 /**
563 * Shows untagged articles for current user.
564 *
565 * @param Request $request
566 * @param int $page
567 *
568 * @Route("/untagged/list/{page}", name="untagged", defaults={"page" = "1"})
569 *
570 * @return \Symfony\Component\HttpFoundation\Response
571 */
572 public function showUntaggedEntriesAction(Request $request, $page)
573 {
574 return $this->showEntries('untagged', $request, $page);
575 }
9d50517c 576}