aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Wallabag
diff options
context:
space:
mode:
Diffstat (limited to 'src/Wallabag')
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php99
-rw-r--r--src/Wallabag/ApiBundle/Controller/UserRestController.php139
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing_rest.yml5
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php16
-rw-r--r--src/Wallabag/CoreBundle/Controller/ConfigController.php4
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php14
-rw-r--r--src/Wallabag/CoreBundle/Controller/TagController.php5
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/Configuration.php2
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php1
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php6
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php5
-rw-r--r--src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php9
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php74
-rw-r--r--src/Wallabag/CoreBundle/Helper/DownloadImages.php9
-rw-r--r--src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php5
-rw-r--r--src/Wallabag/CoreBundle/Helper/Redirect.php10
-rw-r--r--src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php1
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml1
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig40
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig4
-rw-r--r--src/Wallabag/ImportBundle/Command/ImportCommand.php9
-rw-r--r--src/Wallabag/ImportBundle/Import/AbstractImport.php22
-rw-r--r--src/Wallabag/ImportBundle/Import/BrowserImport.php2
-rw-r--r--src/Wallabag/ImportBundle/Import/InstapaperImport.php19
-rw-r--r--src/Wallabag/ImportBundle/Import/PinboardImport.php2
-rw-r--r--src/Wallabag/ImportBundle/Import/PocketImport.php2
-rw-r--r--src/Wallabag/ImportBundle/Import/ReadabilityImport.php2
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagImport.php2
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV1Import.php18
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV2Import.php4
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/services.yml2
-rw-r--r--src/Wallabag/UserBundle/Controller/ManageController.php4
-rw-r--r--src/Wallabag/UserBundle/Entity/User.php38
-rw-r--r--src/Wallabag/UserBundle/EventListener/CreateConfigListener.php3
34 files changed, 418 insertions, 160 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index 31bb67fd..93c8157e 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -231,7 +231,6 @@ class EntryRestController extends WallabagRestController
231 $this->validateAuthentication(); 231 $this->validateAuthentication();
232 232
233 $urls = json_decode($request->query->get('urls', [])); 233 $urls = json_decode($request->query->get('urls', []));
234 $results = [];
235 234
236 $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); 235 $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions');
237 236
@@ -239,32 +238,34 @@ class EntryRestController extends WallabagRestController
239 throw new HttpException(400, 'API limit reached'); 238 throw new HttpException(400, 'API limit reached');
240 } 239 }
241 240
241 $results = [];
242 if (empty($urls)) {
243 return $this->sendResponse($results);
244 }
245
242 // handle multiple urls 246 // handle multiple urls
243 if (!empty($urls)) { 247 foreach ($urls as $key => $url) {
244 foreach ($urls as $key => $url) { 248 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId(
245 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( 249 $url,
246 $url, 250 $this->getUser()->getId()
247 $this->getUser()->getId() 251 );
248 );
249
250 $results[$key]['url'] = $url;
251
252 if (false === $entry) {
253 $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
254 new Entry($this->getUser()),
255 $url
256 );
257 }
258 252
259 $em = $this->getDoctrine()->getManager(); 253 $results[$key]['url'] = $url;
260 $em->persist($entry);
261 $em->flush();
262 254
263 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; 255 if (false === $entry) {
256 $entry = new Entry($this->getUser());
264 257
265 // entry saved, dispatch event about it! 258 $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url);
266 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
267 } 259 }
260
261 $em = $this->getDoctrine()->getManager();
262 $em->persist($entry);
263 $em->flush();
264
265 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false;
266
267 // entry saved, dispatch event about it!
268 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
268 } 269 }
269 270
270 return $this->sendResponse($results); 271 return $this->sendResponse($results);
@@ -273,6 +274,9 @@ class EntryRestController extends WallabagRestController
273 /** 274 /**
274 * Create an entry. 275 * Create an entry.
275 * 276 *
277 * 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**.
278 * Otherwise, content will be fetched as normal from the url and values will be overwritten.
279 *
276 * @ApiDoc( 280 * @ApiDoc(
277 * parameters={ 281 * parameters={
278 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, 282 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
@@ -280,6 +284,11 @@ class EntryRestController extends WallabagRestController
280 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, 284 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
281 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, 285 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
282 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, 286 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
287 * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"},
288 * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"},
289 * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"},
290 * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"},
291 * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"},
283 * } 292 * }
284 * ) 293 * )
285 * 294 *
@@ -291,32 +300,46 @@ class EntryRestController extends WallabagRestController
291 300
292 $url = $request->request->get('url'); 301 $url = $request->request->get('url');
293 $title = $request->request->get('title'); 302 $title = $request->request->get('title');
303 $tags = $request->request->get('tags', []);
294 $isArchived = $request->request->get('archive'); 304 $isArchived = $request->request->get('archive');
295 $isStarred = $request->request->get('starred'); 305 $isStarred = $request->request->get('starred');
306 $content = $request->request->get('content');
307 $language = $request->request->get('language');
308 $picture = $request->request->get('preview_picture');
309 $publishedAt = $request->request->get('published_at');
310 $authors = $request->request->get('authors', '');
296 311
297 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); 312 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId());
298 313
299 if (false === $entry) { 314 if (false === $entry) {
300 $entry = new Entry($this->getUser()); 315 $entry = new Entry($this->getUser());
301 try {
302 $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
303 $entry,
304 $url
305 );
306 } catch (\Exception $e) {
307 $this->get('logger')->error('Error while saving an entry', [
308 'exception' => $e,
309 'entry' => $entry,
310 ]);
311 $entry->setUrl($url);
312 }
313 } 316 }
314 317
315 if (!is_null($title)) { 318 try {
316 $entry->setTitle($title); 319 $this->get('wallabag_core.content_proxy')->updateEntry(
320 $entry,
321 $url,
322 [
323 'title' => $title,
324 'html' => $content,
325 'url' => $url,
326 'language' => $language,
327 'date' => $publishedAt,
328 // faking the preview picture
329 'open_graph' => [
330 'og_image' => $picture,
331 ],
332 'authors' => explode(',', $authors),
333 ]
334 );
335 } catch (\Exception $e) {
336 $this->get('logger')->error('Error while saving an entry', [
337 'exception' => $e,
338 'entry' => $entry,
339 ]);
340 $entry->setUrl($url);
317 } 341 }
318 342
319 $tags = $request->request->get('tags', '');
320 if (!empty($tags)) { 343 if (!empty($tags)) {
321 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); 344 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags);
322 } 345 }
@@ -406,7 +429,7 @@ class EntryRestController extends WallabagRestController
406 $this->validateUserAccess($entry->getUser()->getId()); 429 $this->validateUserAccess($entry->getUser()->getId());
407 430
408 try { 431 try {
409 $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); 432 $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
410 } catch (\Exception $e) { 433 } catch (\Exception $e) {
411 $this->get('logger')->error('Error while saving an entry', [ 434 $this->get('logger')->error('Error while saving an entry', [
412 'exception' => $e, 435 'exception' => $e,
diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php
new file mode 100644
index 00000000..a1b78e3f
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Controller/UserRestController.php
@@ -0,0 +1,139 @@
1<?php
2
3namespace Wallabag\ApiBundle\Controller;
4
5use FOS\UserBundle\Event\UserEvent;
6use FOS\UserBundle\FOSUserEvents;
7use JMS\Serializer\SerializationContext;
8use Nelmio\ApiDocBundle\Annotation\ApiDoc;
9use Symfony\Component\HttpFoundation\Request;
10use Symfony\Component\HttpFoundation\JsonResponse;
11use Wallabag\UserBundle\Entity\User;
12
13class UserRestController extends WallabagRestController
14{
15 /**
16 * Retrieve current logged in user informations.
17 *
18 * @ApiDoc()
19 *
20 * @return JsonResponse
21 */
22 public function getUserAction()
23 {
24 $this->validateAuthentication();
25
26 return $this->sendUser($this->getUser());
27 }
28
29 /**
30 * Register an user.
31 *
32 * @ApiDoc(
33 * requirements={
34 * {"name"="username", "dataType"="string", "required"=true, "description"="The user's username"},
35 * {"name"="password", "dataType"="string", "required"=true, "description"="The user's password"},
36 * {"name"="email", "dataType"="string", "required"=true, "description"="The user's email"}
37 * }
38 * )
39 *
40 * @todo Make this method (or the whole API) accessible only through https
41 *
42 * @return JsonResponse
43 */
44 public function putUserAction(Request $request)
45 {
46 if (!$this->container->getParameter('fosuser_registration')) {
47 $json = $this->get('serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json');
48
49 return (new JsonResponse())->setJson($json)->setStatusCode(403);
50 }
51
52 $userManager = $this->get('fos_user.user_manager');
53 $user = $userManager->createUser();
54 // enable created user by default
55 $user->setEnabled(true);
56
57 $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [
58 'csrf_protection' => false,
59 ]);
60
61 // simulate form submission
62 $form->submit([
63 'username' => $request->request->get('username'),
64 'plainPassword' => [
65 'first' => $request->request->get('password'),
66 'second' => $request->request->get('password'),
67 ],
68 'email' => $request->request->get('email'),
69 ]);
70
71 if ($form->isSubmitted() && false === $form->isValid()) {
72 $view = $this->view($form, 400);
73 $view->setFormat('json');
74
75 // handle errors in a more beautiful way than the default view
76 $data = json_decode($this->handleView($view)->getContent(), true)['children'];
77 $errors = [];
78
79 if (isset($data['username']['errors'])) {
80 $errors['username'] = $this->translateErrors($data['username']['errors']);
81 }
82
83 if (isset($data['email']['errors'])) {
84 $errors['email'] = $this->translateErrors($data['email']['errors']);
85 }
86
87 if (isset($data['plainPassword']['children']['first']['errors'])) {
88 $errors['password'] = $this->translateErrors($data['plainPassword']['children']['first']['errors']);
89 }
90
91 $json = $this->get('serializer')->serialize(['error' => $errors], 'json');
92
93 return (new JsonResponse())->setJson($json)->setStatusCode(400);
94 }
95
96 $userManager->updateUser($user);
97
98 // dispatch a created event so the associated config will be created
99 $event = new UserEvent($user, $request);
100 $this->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event);
101
102 return $this->sendUser($user);
103 }
104
105 /**
106 * Send user response.
107 *
108 * @param User $user
109 *
110 * @return JsonResponse
111 */
112 private function sendUser(User $user)
113 {
114 $json = $this->get('serializer')->serialize(
115 $user,
116 'json',
117 SerializationContext::create()->setGroups(['user_api'])
118 );
119
120 return (new JsonResponse())->setJson($json);
121 }
122
123 /**
124 * Translate errors message.
125 *
126 * @param array $errors
127 *
128 * @return array
129 */
130 private function translateErrors($errors)
131 {
132 $translatedErrors = [];
133 foreach ($errors as $error) {
134 $translatedErrors[] = $this->get('translator')->trans($error);
135 }
136
137 return $translatedErrors;
138 }
139}
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
21user:
22 type: rest
23 resource: "WallabagApiBundle:UserRest"
24 name_prefix: api_
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index 0d9364f6..d9608246 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -499,20 +499,18 @@ class InstallCommand extends ContainerAwareCommand
499 $output = new BufferedOutput(); 499 $output = new BufferedOutput();
500 $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); 500 $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output);
501 501
502 // PDO does not always close the connection after Doctrine commands.
503 // See https://github.com/symfony/symfony/issues/11750.
504 $this->getContainer()->get('doctrine')->getManager()->getConnection()->close();
505
502 if (0 !== $exitCode) { 506 if (0 !== $exitCode) {
503 $this->getApplication()->setAutoExit(true); 507 $this->getApplication()->setAutoExit(true);
504 508
505 $this->defaultOutput->writeln(''); 509 throw new \RuntimeException(
506 $this->defaultOutput->writeln('<error>The command "'.$command.'" generates some errors: </error>'); 510 'The command "'.$command."\" generates some errors: \n\n"
507 $this->defaultOutput->writeln($output->fetch()); 511 .$output->fetch());
508
509 die();
510 } 512 }
511 513
512 // PDO does not always close the connection after Doctrine commands.
513 // See https://github.com/symfony/symfony/issues/11750.
514 $this->getContainer()->get('doctrine')->getManager()->getConnection()->close();
515
516 return $this; 514 return $this;
517 } 515 }
518 516
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
index 1a80cc1a..0e61c642 100644
--- a/src/Wallabag/CoreBundle/Controller/ConfigController.php
+++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php
@@ -250,7 +250,7 @@ class ConfigController extends Controller
250 case 'entries': 250 case 'entries':
251 // SQLite doesn't care about cascading remove, so we need to manually remove associated stuff 251 // SQLite doesn't care about cascading remove, so we need to manually remove associated stuff
252 // otherwise they won't be removed ... 252 // otherwise they won't be removed ...
253 if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { 253 if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) {
254 $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); 254 $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId());
255 } 255 }
256 256
@@ -262,7 +262,7 @@ class ConfigController extends Controller
262 ->removeAllByUserId($this->getUser()->getId()); 262 ->removeAllByUserId($this->getUser()->getId());
263 break; 263 break;
264 case 'archived': 264 case 'archived':
265 if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { 265 if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) {
266 $this->removeAnnotationsForArchivedByUserId($this->getUser()->getId()); 266 $this->removeAnnotationsForArchivedByUserId($this->getUser()->getId());
267 } 267 }
268 268
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 8d2ac6d4..fafa49f1 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -53,22 +53,17 @@ class EntryController extends Controller
53 53
54 /** 54 /**
55 * Fetch content and update entry. 55 * Fetch content and update entry.
56 * In case it fails, entry will return to avod loosing the data. 56 * In case it fails, $entry->getContent will return an error message.
57 * 57 *
58 * @param Entry $entry 58 * @param Entry $entry
59 * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded 59 * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded
60 *
61 * @return Entry
62 */ 60 */
63 private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') 61 private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved')
64 { 62 {
65 // put default title in case of fetching content failed
66 $entry->setTitle('No title found');
67
68 $message = 'flashes.entry.notice.'.$prefixMessage; 63 $message = 'flashes.entry.notice.'.$prefixMessage;
69 64
70 try { 65 try {
71 $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); 66 $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
72 } catch (\Exception $e) { 67 } catch (\Exception $e) {
73 $this->get('logger')->error('Error while saving an entry', [ 68 $this->get('logger')->error('Error while saving an entry', [
74 'exception' => $e, 69 'exception' => $e,
@@ -79,8 +74,6 @@ class EntryController extends Controller
79 } 74 }
80 75
81 $this->get('session')->getFlashBag()->add('notice', $message); 76 $this->get('session')->getFlashBag()->add('notice', $message);
82
83 return $entry;
84 } 77 }
85 78
86 /** 79 /**
@@ -321,8 +314,7 @@ class EntryController extends Controller
321 314
322 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); 315 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false);
323 316
324 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') 317 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter);
325 ->prepare($pagerAdapter, $page);
326 318
327 try { 319 try {
328 $entries->setCurrentPage($page); 320 $entries->setCurrentPage($page);
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php
index fb6a720b..9422bae4 100644
--- a/src/Wallabag/CoreBundle/Controller/TagController.php
+++ b/src/Wallabag/CoreBundle/Controller/TagController.php
@@ -70,7 +70,7 @@ class TagController extends Controller
70 $em->flush(); 70 $em->flush();
71 } 71 }
72 72
73 $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer')); 73 $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer'), '', true);
74 74
75 return $this->redirect($redirectUrl); 75 return $this->redirect($redirectUrl);
76 } 76 }
@@ -125,8 +125,7 @@ class TagController extends Controller
125 125
126 $pagerAdapter = new ArrayAdapter($entriesByTag); 126 $pagerAdapter = new ArrayAdapter($entriesByTag);
127 127
128 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') 128 $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter);
129 ->prepare($pagerAdapter, $page);
130 129
131 try { 130 try {
132 $entries->setCurrentPage($page); 131 $entries->setCurrentPage($page);
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
index 75b37729..8b5b5744 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
+++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
@@ -41,6 +41,8 @@ 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()
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
index c075c19f..a2a703cb 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.fetching_error_message_title', $config['fetching_error_message_title']);
29 $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']); 30 $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']);
30 31
31 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 32 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index 08a67c34..9a7dd4e7 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -550,7 +550,7 @@ class Entry
550 } 550 }
551 551
552 /** 552 /**
553 * @return ArrayCollection<Tag> 553 * @return ArrayCollection
554 */ 554 */
555 public function getTags() 555 public function getTags()
556 { 556 {
@@ -685,7 +685,7 @@ class Entry
685 } 685 }
686 686
687 /** 687 /**
688 * @return int 688 * @return string
689 */ 689 */
690 public function getHttpStatus() 690 public function getHttpStatus()
691 { 691 {
@@ -693,7 +693,7 @@ class Entry
693 } 693 }
694 694
695 /** 695 /**
696 * @param int $httpStatus 696 * @param string $httpStatus
697 * 697 *
698 * @return Entry 698 * @return Entry
699 */ 699 */
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
index 3b4c4cf9..5e6af8cc 100644
--- a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
@@ -45,9 +45,8 @@ class SQLiteCascadeDeleteSubscriber implements EventSubscriber
45 public function preRemove(LifecycleEventArgs $args) 45 public function preRemove(LifecycleEventArgs $args)
46 { 46 {
47 $entity = $args->getEntity(); 47 $entity = $args->getEntity();
48 48 if (!$this->doctrine->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform
49 if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver || 49 || !$entity instanceof Entry) {
50 !$entity instanceof Entry) {
51 return; 50 return;
52 } 51 }
53 52
diff --git a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
index c712bb26..1c56fa9f 100644
--- a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
+++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
@@ -5,7 +5,6 @@ namespace Wallabag\CoreBundle\GuzzleSiteAuthenticator;
5use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig; 5use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig;
6use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfigBuilder; 6use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfigBuilder;
7use Graby\SiteConfig\ConfigBuilder; 7use Graby\SiteConfig\ConfigBuilder;
8use OutOfRangeException;
9use Psr\Log\LoggerInterface; 8use Psr\Log\LoggerInterface;
10 9
11class GrabySiteConfigBuilder implements SiteConfigBuilder 10class GrabySiteConfigBuilder implements SiteConfigBuilder
@@ -38,13 +37,7 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder
38 } 37 }
39 38
40 /** 39 /**
41 * Builds the SiteConfig for a host. 40 * {@inheritdoc}
42 *
43 * @param string $host The "www." prefix is ignored
44 *
45 * @return SiteConfig
46 *
47 * @throws OutOfRangeException If there is no config for $host
48 */ 41 */
49 public function buildForHost($host) 42 public function buildForHost($host)
50 { 43 {
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index 4b3e6fbb..bfaa1976 100644
--- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php
+++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
@@ -31,22 +31,20 @@ class ContentProxy
31 } 31 }
32 32
33 /** 33 /**
34 * Fetch content using graby and hydrate given entry with results information. 34 * Update entry using either fetched or provided content.
35 * In case we couldn't find content, we'll try to use Open Graph data.
36 * 35 *
37 * We can also force the content, in case of an import from the v1 for example, so the function won't 36 * @param Entry $entry Entry to update
38 * fetch the content from the website but rather use information given with the $content parameter. 37 * @param string $url Url of the content
39 * 38 * @param array $content Array with content provided for import with AT LEAST keys title, html, url to skip the fetchContent from the url
40 * @param Entry $entry Entry to update 39 * @param bool $disableContentUpdate Whether to skip trying to fetch content using Graby
41 * @param string $url Url to grab content for
42 * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url
43 *
44 * @return Entry
45 */ 40 */
46 public function updateEntry(Entry $entry, $url, array $content = []) 41 public function updateEntry(Entry $entry, $url, array $content = [], $disableContentUpdate = false)
47 { 42 {
48 // do we have to fetch the content or the provided one is ok? 43 if (!empty($content['html'])) {
49 if (empty($content) || false === $this->validateContent($content)) { 44 $content['html'] = $this->graby->cleanupHtml($content['html'], $url);
45 }
46
47 if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) {
50 $fetchedContent = $this->graby->fetchContent($url); 48 $fetchedContent = $this->graby->fetchContent($url);
51 49
52 // when content is imported, we have information in $content 50 // when content is imported, we have information in $content
@@ -56,8 +54,24 @@ class ContentProxy
56 } 54 }
57 } 55 }
58 56
57 // be sure to keep the url in case of error
58 // so we'll be able to refetch it in the future
59 $content['url'] = !empty($content['url']) ? $content['url'] : $url;
60
61 $this->stockEntry($entry, $content);
62 }
63
64 /**
65 * Stock entry with fetched or imported content.
66 * Will fall back to OpenGraph data if available.
67 *
68 * @param Entry $entry Entry to stock
69 * @param array $content Array with at least title, url & html
70 */
71 private function stockEntry(Entry $entry, array $content)
72 {
59 $title = $content['title']; 73 $title = $content['title'];
60 if (!$title && isset($content['open_graph']['og_title'])) { 74 if (!$title && !empty($content['open_graph']['og_title'])) {
61 $title = $content['open_graph']['og_title']; 75 $title = $content['open_graph']['og_title'];
62 } 76 }
63 77
@@ -65,19 +79,30 @@ class ContentProxy
65 if (false === $html) { 79 if (false === $html) {
66 $html = $this->fetchingErrorMessage; 80 $html = $this->fetchingErrorMessage;
67 81
68 if (isset($content['open_graph']['og_description'])) { 82 if (!empty($content['open_graph']['og_description'])) {
69 $html .= '<p><i>But we found a short description: </i></p>'; 83 $html .= '<p><i>But we found a short description: </i></p>';
70 $html .= $content['open_graph']['og_description']; 84 $html .= $content['open_graph']['og_description'];
71 } 85 }
72 } 86 }
73 87
74 $entry->setUrl($content['url'] ?: $url); 88 $entry->setUrl($content['url']);
75 $entry->setTitle($title); 89 $entry->setTitle($title);
76 $entry->setContent($html); 90 $entry->setContent($html);
77 $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); 91 $entry->setHttpStatus(isset($content['status']) ? $content['status'] : '');
78 92
79 if (isset($content['date']) && null !== $content['date'] && '' !== $content['date']) { 93 if (!empty($content['date'])) {
80 $entry->setPublishedAt(new \DateTime($content['date'])); 94 $date = $content['date'];
95
96 // is it a timestamp?
97 if (filter_var($date, FILTER_VALIDATE_INT) !== false) {
98 $date = '@'.$content['date'];
99 }
100
101 try {
102 $entry->setPublishedAt(new \DateTime($date));
103 } catch (\Exception $e) {
104 $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $content['url'], 'date' => $content['date']]);
105 }
81 } 106 }
82 107
83 if (!empty($content['authors'])) { 108 if (!empty($content['authors'])) {
@@ -97,12 +122,12 @@ class ContentProxy
97 $entry->setDomainName($domainName); 122 $entry->setDomainName($domainName);
98 } 123 }
99 124
100 if (isset($content['open_graph']['og_image']) && $content['open_graph']['og_image']) { 125 if (!empty($content['open_graph']['og_image'])) {
101 $entry->setPreviewPicture($content['open_graph']['og_image']); 126 $entry->setPreviewPicture($content['open_graph']['og_image']);
102 } 127 }
103 128
104 // if content is an image define as a preview too 129 // if content is an image define as a preview too
105 if (isset($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { 130 if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
106 $entry->setPreviewPicture($content['url']); 131 $entry->setPreviewPicture($content['url']);
107 } 132 }
108 133
@@ -110,17 +135,14 @@ class ContentProxy
110 $this->tagger->tag($entry); 135 $this->tagger->tag($entry);
111 } catch (\Exception $e) { 136 } catch (\Exception $e) {
112 $this->logger->error('Error while trying to automatically tag an entry.', [ 137 $this->logger->error('Error while trying to automatically tag an entry.', [
113 'entry_url' => $url, 138 'entry_url' => $content['url'],
114 'error_msg' => $e->getMessage(), 139 'error_msg' => $e->getMessage(),
115 ]); 140 ]);
116 } 141 }
117
118 return $entry;
119 } 142 }
120 143
121 /** 144 /**
122 * Validate that the given content as enough value to be used 145 * Validate that the given content has at least a title, an html and a url.
123 * instead of fetch the content from the url.
124 * 146 *
125 * @param array $content 147 * @param array $content
126 * 148 *
@@ -128,6 +150,6 @@ class ContentProxy
128 */ 150 */
129 private function validateContent(array $content) 151 private function validateContent(array $content)
130 { 152 {
131 return isset($content['title']) && isset($content['html']) && isset($content['url']) && isset($content['language']) && isset($content['content_type']); 153 return !empty($content['title']) && !empty($content['html']) && !empty($content['url']);
132 } 154 }
133} 155}
diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
index 0d330d2a..54e23a05 100644
--- a/src/Wallabag/CoreBundle/Helper/DownloadImages.php
+++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
@@ -54,7 +54,7 @@ class DownloadImages
54 $crawler = new Crawler($html); 54 $crawler = new Crawler($html);
55 $result = $crawler 55 $result = $crawler
56 ->filterXpath('//img') 56 ->filterXpath('//img')
57 ->extract(array('src')); 57 ->extract(['src']);
58 58
59 $relativePath = $this->getRelativePath($entryId); 59 $relativePath = $this->getRelativePath($entryId);
60 60
@@ -66,6 +66,11 @@ class DownloadImages
66 continue; 66 continue;
67 } 67 }
68 68
69 // if image contains "&" and we can't find it in the html it might be because it's encoded as &amp;
70 if (false !== stripos($image, '&') && false === stripos($html, $image)) {
71 $image = str_replace('&', '&amp;', $image);
72 }
73
69 $html = str_replace($image, $imagePath, $html); 74 $html = str_replace($image, $imagePath, $html);
70 } 75 }
71 76
@@ -114,7 +119,7 @@ class DownloadImages
114 $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); 119 $ext = $this->mimeGuesser->guess($res->getHeader('content-type'));
115 $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); 120 $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]);
116 if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) { 121 if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) {
117 $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping '.$imagePath); 122 $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping: '.$imagePath);
118 123
119 return false; 124 return false;
120 } 125 }
diff --git a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php
index 7d3798b9..df579ebd 100644
--- a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php
+++ b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php
@@ -20,16 +20,15 @@ class PreparePagerForEntries
20 20
21 /** 21 /**
22 * @param AdapterInterface $adapter 22 * @param AdapterInterface $adapter
23 * @param int $page
24 * 23 *
25 * @return null|Pagerfanta 24 * @return null|Pagerfanta
26 */ 25 */
27 public function prepare(AdapterInterface $adapter, $page = 1) 26 public function prepare(AdapterInterface $adapter)
28 { 27 {
29 $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; 28 $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
30 29
31 if (null === $user || !is_object($user)) { 30 if (null === $user || !is_object($user)) {
32 return null; 31 return;
33 } 32 }
34 33
35 $entries = new Pagerfanta($adapter); 34 $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 add27db2..509d0dec 100644
--- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php
+++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php
@@ -15,6 +15,7 @@ class RuleBasedTagger
15 private $rulerz; 15 private $rulerz;
16 private $tagRepository; 16 private $tagRepository;
17 private $entryRepository; 17 private $entryRepository;
18 private $logger;
18 19
19 public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository, LoggerInterface $logger) 20 public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository, LoggerInterface $logger)
20 { 21 {
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index a68b2fdc..a9b0d2d5 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:
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 174b7b54..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 }}" 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 </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/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
index b2d91c9c..6f657b18 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
@@ -24,9 +24,9 @@
24 </div> 24 </div>
25 25
26 <br /> 26 <br />
27 <ul class="row data"> 27 <ul class="{% if listMode == 1 %}collection{% else %}row data{% endif %}">
28 {% for entry in entries %} 28 {% for entry in entries %}
29 <li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% endif %} s12"> 29 <li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% else %}collection-item{% endif %} s12">
30 {% if listMode == 1 %} 30 {% if listMode == 1 %}
31 {% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %} 31 {% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %}
32 {% elseif entry.previewPicture is null %} 32 {% elseif entry.previewPicture is null %}
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php
index ce72837a..5f1ab0af 100644
--- a/src/Wallabag/ImportBundle/Command/ImportCommand.php
+++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php
@@ -5,6 +5,7 @@ namespace Wallabag\ImportBundle\Command;
5use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 5use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
6use Symfony\Component\Config\Definition\Exception\Exception; 6use Symfony\Component\Config\Definition\Exception\Exception;
7use Symfony\Component\Console\Input\InputArgument; 7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputOption;
8use Symfony\Component\Console\Input\InputInterface; 9use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface; 10use Symfony\Component\Console\Output\OutputInterface;
10 11
@@ -17,9 +18,10 @@ class ImportCommand extends ContainerAwareCommand
17 ->setDescription('Import entries from a JSON export') 18 ->setDescription('Import entries from a JSON export')
18 ->addArgument('username', InputArgument::REQUIRED, 'User 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)
22 ->addOption('useUserId', null, InputArgument::OPTIONAL, 'Use user id instead of username to find account', 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')
23 ; 25 ;
24 } 26 }
25 27
@@ -69,6 +71,7 @@ class ImportCommand extends ContainerAwareCommand
69 } 71 }
70 72
71 $import->setMarkAsRead($input->getOption('markAsRead')); 73 $import->setMarkAsRead($input->getOption('markAsRead'));
74 $import->setDisableContentUpdate($input->getOption('disableContentUpdate'));
72 $import->setUser($user); 75 $import->setUser($user);
73 76
74 $res = $import 77 $res = $import
diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php
index a61388c0..9b624296 100644
--- a/src/Wallabag/ImportBundle/Import/AbstractImport.php
+++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php
@@ -24,6 +24,7 @@ abstract class AbstractImport implements ImportInterface
24 protected $producer; 24 protected $producer;
25 protected $user; 25 protected $user;
26 protected $markAsRead; 26 protected $markAsRead;
27 protected $disableContentUpdate = false;
27 protected $skippedEntries = 0; 28 protected $skippedEntries = 0;
28 protected $importedEntries = 0; 29 protected $importedEntries = 0;
29 protected $queuedEntries = 0; 30 protected $queuedEntries = 0;
@@ -85,21 +86,34 @@ abstract class AbstractImport implements ImportInterface
85 } 86 }
86 87
87 /** 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 /**
88 * Fetch content from the ContentProxy (using graby). 101 * Fetch content from the ContentProxy (using graby).
89 * If it fails return the given entry to be saved in all case (to avoid user to loose the content). 102 * If it fails return the given entry to be saved in all case (to avoid user to loose the content).
90 * 103 *
91 * @param Entry $entry Entry to update 104 * @param Entry $entry Entry to update
92 * @param string $url Url to grab content for 105 * @param string $url Url to grab content for
93 * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url 106 * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url
94 *
95 * @return Entry
96 */ 107 */
97 protected function fetchContent(Entry $entry, $url, array $content = []) 108 protected function fetchContent(Entry $entry, $url, array $content = [])
98 { 109 {
99 try { 110 try {
100 return $this->contentProxy->updateEntry($entry, $url, $content); 111 $this->contentProxy->updateEntry($entry, $url, $content, $this->disableContentUpdate);
101 } catch (\Exception $e) { 112 } catch (\Exception $e) {
102 return $entry; 113 $this->logger->error('Error trying to import an entry.', [
114 'entry_url' => $url,
115 'error_msg' => $e->getMessage(),
116 ]);
103 } 117 }
104 } 118 }
105 119
diff --git a/src/Wallabag/ImportBundle/Import/BrowserImport.php b/src/Wallabag/ImportBundle/Import/BrowserImport.php
index ef0eeb7e..71e65e59 100644
--- a/src/Wallabag/ImportBundle/Import/BrowserImport.php
+++ b/src/Wallabag/ImportBundle/Import/BrowserImport.php
@@ -201,7 +201,7 @@ abstract class BrowserImport extends AbstractImport
201 $entry->setTitle($data['title']); 201 $entry->setTitle($data['title']);
202 202
203 // update entry with content (in case fetching failed, the given entry will be return) 203 // update entry with content (in case fetching failed, the given entry will be return)
204 $entry = $this->fetchContent($entry, $data['url'], $data); 204 $this->fetchContent($entry, $data['url'], $data);
205 205
206 if (array_key_exists('tags', $data)) { 206 if (array_key_exists('tags', $data)) {
207 $this->tagsAssigner->assignTagsToEntry( 207 $this->tagsAssigner->assignTagsToEntry(
diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
index 70a53f1a..3aa12f6f 100644
--- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php
+++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
@@ -68,6 +68,14 @@ class InstapaperImport extends AbstractImport
68 continue; 68 continue;
69 } 69 }
70 70
71 // last element in the csv is the folder where the content belong
72 // BUT it can also be the status (since status = folder in Instapaper)
73 // and we don't want archive, unread & starred to become a tag
74 $tags = null;
75 if (false === in_array($data[3], ['Archive', 'Unread', 'Starred'])) {
76 $tags = [$data[3]];
77 }
78
71 $entries[] = [ 79 $entries[] = [
72 'url' => $data[0], 80 'url' => $data[0],
73 'title' => $data[1], 81 'title' => $data[1],
@@ -75,6 +83,7 @@ class InstapaperImport extends AbstractImport
75 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred', 83 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred',
76 'is_starred' => $data[3] === 'Starred', 84 'is_starred' => $data[3] === 'Starred',
77 'html' => false, 85 'html' => false,
86 'tags' => $tags,
78 ]; 87 ];
79 } 88 }
80 fclose($handle); 89 fclose($handle);
@@ -116,7 +125,15 @@ class InstapaperImport extends AbstractImport
116 $entry->setTitle($importedEntry['title']); 125 $entry->setTitle($importedEntry['title']);
117 126
118 // update entry with content (in case fetching failed, the given entry will be return) 127 // update entry with content (in case fetching failed, the given entry will be return)
119 $entry = $this->fetchContent($entry, $importedEntry['url'], $importedEntry); 128 $this->fetchContent($entry, $importedEntry['url'], $importedEntry);
129
130 if (!empty($importedEntry['tags'])) {
131 $this->tagsAssigner->assignTagsToEntry(
132 $entry,
133 $importedEntry['tags'],
134 $this->em->getUnitOfWork()->getScheduledEntityInsertions()
135 );
136 }
120 137
121 $entry->setArchived($importedEntry['is_archived']); 138 $entry->setArchived($importedEntry['is_archived']);
122 $entry->setStarred($importedEntry['is_starred']); 139 $entry->setStarred($importedEntry['is_starred']);
diff --git a/src/Wallabag/ImportBundle/Import/PinboardImport.php b/src/Wallabag/ImportBundle/Import/PinboardImport.php
index 489b9257..110b0464 100644
--- a/src/Wallabag/ImportBundle/Import/PinboardImport.php
+++ b/src/Wallabag/ImportBundle/Import/PinboardImport.php
@@ -109,7 +109,7 @@ 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->tagsAssigner->assignTagsToEntry( 115 $this->tagsAssigner->assignTagsToEntry(
diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php
index 8835161b..c1d5b6da 100644
--- a/src/Wallabag/ImportBundle/Import/PocketImport.php
+++ b/src/Wallabag/ImportBundle/Import/PocketImport.php
@@ -192,7 +192,7 @@ class PocketImport extends AbstractImport
192 $entry->setUrl($url); 192 $entry->setUrl($url);
193 193
194 // update entry with content (in case fetching failed, the given entry will be return) 194 // update entry with content (in case fetching failed, the given entry will be return)
195 $entry = $this->fetchContent($entry, $url); 195 $this->fetchContent($entry, $url);
196 196
197 // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted 197 // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted
198 $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead); 198 $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead);
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 0e5382cf..c64ccd64 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagImport.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagImport.php
@@ -108,7 +108,7 @@ 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->tagsAssigner->assignTagsToEntry( 114 $this->tagsAssigner->assignTagsToEntry(
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
index 59e3ce02..1f0df646 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
@@ -4,6 +4,17 @@ namespace Wallabag\ImportBundle\Import;
4 4
5class WallabagV1Import extends WallabagImport 5class WallabagV1Import extends WallabagImport
6{ 6{
7 protected $fetchingErrorMessage;
8 protected $fetchingErrorMessageTitle;
9
10 public function __construct($em, $contentProxy, $tagsAssigner, $eventDispatcher, $fetchingErrorMessageTitle, $fetchingErrorMessage)
11 {
12 $this->fetchingErrorMessageTitle = $fetchingErrorMessageTitle;
13 $this->fetchingErrorMessage = $fetchingErrorMessage;
14
15 parent::__construct($em, $contentProxy, $tagsAssigner, $eventDispatcher);
16 }
17
7 /** 18 /**
8 * {@inheritdoc} 19 * {@inheritdoc}
9 */ 20 */
@@ -43,10 +54,11 @@ class WallabagV1Import extends WallabagImport
43 'created_at' => '', 54 'created_at' => '',
44 ]; 55 ];
45 56
46 // force content to be refreshed in case on bad fetch in the v1 installation 57 // In case of a bad fetch in v1, replace title and content with v2 error strings
58 // If fetching fails again, they will get this instead of the v1 strings
47 if (in_array($entry['title'], $this->untitled)) { 59 if (in_array($entry['title'], $this->untitled)) {
48 $data['title'] = ''; 60 $data['title'] = $this->fetchingErrorMessageTitle;
49 $data['html'] = ''; 61 $data['html'] = $this->fetchingErrorMessage;
50 } 62 }
51 63
52 if (array_key_exists('tags', $entry) && $entry['tags'] != '') { 64 if (array_key_exists('tags', $entry) && $entry['tags'] != '') {
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
index d2a89d79..3e085ecf 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
@@ -36,8 +36,8 @@ class WallabagV2Import extends WallabagImport
36 return [ 36 return [
37 'html' => $entry['content'], 37 'html' => $entry['content'],
38 'content_type' => $entry['mimetype'], 38 'content_type' => $entry['mimetype'],
39 'is_archived' => (int) ($entry['is_archived'] || $this->markAsRead), 39 'is_archived' => (bool) ($entry['is_archived'] || $this->markAsRead),
40 'is_starred' => false, 40 'is_starred' => (bool) $entry['is_starred'],
41 ] + $entry; 41 ] + $entry;
42 } 42 }
43 43
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml
index 661dc7e1..b224a6a2 100644
--- a/src/Wallabag/ImportBundle/Resources/config/services.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/services.yml
@@ -35,6 +35,8 @@ services:
35 - "@wallabag_core.content_proxy" 35 - "@wallabag_core.content_proxy"
36 - "@wallabag_core.tags_assigner" 36 - "@wallabag_core.tags_assigner"
37 - "@event_dispatcher" 37 - "@event_dispatcher"
38 - "%wallabag_core.fetching_error_message_title%"
39 - "%wallabag_core.fetching_error_message%"
38 calls: 40 calls:
39 - [ setLogger, [ "@logger" ]] 41 - [ setLogger, [ "@logger" ]]
40 tags: 42 tags:
diff --git a/src/Wallabag/UserBundle/Controller/ManageController.php b/src/Wallabag/UserBundle/Controller/ManageController.php
index 1c5c86d4..084f2c67 100644
--- a/src/Wallabag/UserBundle/Controller/ManageController.php
+++ b/src/Wallabag/UserBundle/Controller/ManageController.php
@@ -33,9 +33,7 @@ class ManageController extends Controller
33 // enable created user by default 33 // enable created user by default
34 $user->setEnabled(true); 34 $user->setEnabled(true);
35 35
36 $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ 36 $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user);
37 'validation_groups' => ['Profile'],
38 ]);
39 $form->handleRequest($request); 37 $form->handleRequest($request);
40 38
41 if ($form->isSubmitted() && $form->isValid()) { 39 if ($form->isSubmitted() && $form->isValid()) {
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php
index 3a167de7..ed6ce331 100644
--- a/src/Wallabag/UserBundle/Entity/User.php
+++ b/src/Wallabag/UserBundle/Entity/User.php
@@ -4,11 +4,11 @@ namespace Wallabag\UserBundle\Entity;
4 4
5use Doctrine\Common\Collections\ArrayCollection; 5use Doctrine\Common\Collections\ArrayCollection;
6use Doctrine\ORM\Mapping as ORM; 6use Doctrine\ORM\Mapping as ORM;
7use JMS\Serializer\Annotation\Groups;
8use JMS\Serializer\Annotation\XmlRoot;
7use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; 9use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
8use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; 10use Scheb\TwoFactorBundle\Model\TrustedComputerInterface;
9use FOS\UserBundle\Model\User as BaseUser; 11use FOS\UserBundle\Model\User as BaseUser;
10use JMS\Serializer\Annotation\ExclusionPolicy;
11use JMS\Serializer\Annotation\Expose;
12use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; 12use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
13use Symfony\Component\Security\Core\User\UserInterface; 13use Symfony\Component\Security\Core\User\UserInterface;
14use Wallabag\ApiBundle\Entity\Client; 14use Wallabag\ApiBundle\Entity\Client;
@@ -18,23 +18,25 @@ use Wallabag\CoreBundle\Entity\Entry;
18/** 18/**
19 * User. 19 * User.
20 * 20 *
21 * @XmlRoot("user")
21 * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") 22 * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository")
22 * @ORM\Table(name="`user`") 23 * @ORM\Table(name="`user`")
23 * @ORM\HasLifecycleCallbacks() 24 * @ORM\HasLifecycleCallbacks()
24 * @ExclusionPolicy("all")
25 * 25 *
26 * @UniqueEntity("email") 26 * @UniqueEntity("email")
27 * @UniqueEntity("username") 27 * @UniqueEntity("username")
28 */ 28 */
29class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface 29class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface
30{ 30{
31 /** @Serializer\XmlAttribute */
31 /** 32 /**
32 * @var int 33 * @var int
33 * 34 *
34 * @Expose
35 * @ORM\Column(name="id", type="integer") 35 * @ORM\Column(name="id", type="integer")
36 * @ORM\Id 36 * @ORM\Id
37 * @ORM\GeneratedValue(strategy="AUTO") 37 * @ORM\GeneratedValue(strategy="AUTO")
38 *
39 * @Groups({"user_api"})
38 */ 40 */
39 protected $id; 41 protected $id;
40 42
@@ -42,20 +44,40 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
42 * @var string 44 * @var string
43 * 45 *
44 * @ORM\Column(name="name", type="text", nullable=true) 46 * @ORM\Column(name="name", type="text", nullable=true)
47 *
48 * @Groups({"user_api"})
45 */ 49 */
46 protected $name; 50 protected $name;
47 51
48 /** 52 /**
49 * @var date 53 * @var string
54 *
55 * @Groups({"user_api"})
56 */
57 protected $username;
58
59 /**
60 * @var string
61 *
62 * @Groups({"user_api"})
63 */
64 protected $email;
65
66 /**
67 * @var \DateTime
50 * 68 *
51 * @ORM\Column(name="created_at", type="datetime") 69 * @ORM\Column(name="created_at", type="datetime")
70 *
71 * @Groups({"user_api"})
52 */ 72 */
53 protected $createdAt; 73 protected $createdAt;
54 74
55 /** 75 /**
56 * @var date 76 * @var \DateTime
57 * 77 *
58 * @ORM\Column(name="updated_at", type="datetime") 78 * @ORM\Column(name="updated_at", type="datetime")
79 *
80 * @Groups({"user_api"})
59 */ 81 */
60 protected $updatedAt; 82 protected $updatedAt;
61 83
@@ -135,7 +157,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
135 } 157 }
136 158
137 /** 159 /**
138 * @return string 160 * @return \DateTime
139 */ 161 */
140 public function getCreatedAt() 162 public function getCreatedAt()
141 { 163 {
@@ -143,7 +165,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
143 } 165 }
144 166
145 /** 167 /**
146 * @return string 168 * @return \DateTime
147 */ 169 */
148 public function getUpdatedAt() 170 public function getUpdatedAt()
149 { 171 {
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;
5use Doctrine\ORM\EntityManager; 5use Doctrine\ORM\EntityManager;
6use FOS\UserBundle\Event\UserEvent; 6use FOS\UserBundle\Event\UserEvent;
7use FOS\UserBundle\FOSUserEvents; 7use FOS\UserBundle\FOSUserEvents;
8use Symfony\Component\EventDispatcher\EventDispatcherInterface;
9use Symfony\Component\EventDispatcher\EventSubscriberInterface; 8use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10use Wallabag\CoreBundle\Entity\Config; 9use 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);