aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorJeremy Benoist <j0k3r@users.noreply.github.com>2016-09-19 07:15:40 +0200
committerGitHub <noreply@github.com>2016-09-19 07:15:40 +0200
commitda18a4682f124b02278860d23ac1d59dee995277 (patch)
treeeabbe9da7203eea41e0cb0ec3c26b9b6599cf58f /src
parent0ed8ce55b5caf2c88e8330afa83abef6c4aac9a4 (diff)
parent59b97fae996d8307b9d957d210d46200f6d206bf (diff)
downloadwallabag-da18a4682f124b02278860d23ac1d59dee995277.tar.gz
wallabag-da18a4682f124b02278860d23ac1d59dee995277.tar.zst
wallabag-da18a4682f124b02278860d23ac1d59dee995277.zip
Merge pull request #1941 from wallabag/v2-asynchronous-jobs
Use asynchronous jobs for imports
Diffstat (limited to 'src')
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php9
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php50
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php3
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php9
-rw-r--r--src/Wallabag/CoreBundle/Entity/Config.php31
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php23
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/ConfigType.php3
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml14
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml12
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml12
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml12
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig12
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig12
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Command/ImportCommand.php6
-rw-r--r--src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php44
-rw-r--r--src/Wallabag/ImportBundle/Consumer/AMQPEntryConsumer.php17
-rw-r--r--src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php74
-rw-r--r--src/Wallabag/ImportBundle/Consumer/RedisEntryConsumer.php29
-rw-r--r--src/Wallabag/ImportBundle/Controller/ImportController.php62
-rw-r--r--src/Wallabag/ImportBundle/Controller/PocketController.php35
-rw-r--r--src/Wallabag/ImportBundle/Controller/ReadabilityController.php16
-rw-r--r--src/Wallabag/ImportBundle/Controller/WallabagController.php10
-rw-r--r--src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php10
-rw-r--r--src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php10
-rw-r--r--src/Wallabag/ImportBundle/Form/Type/UploadImportType.php1
-rw-r--r--src/Wallabag/ImportBundle/Import/AbstractImport.php144
-rw-r--r--src/Wallabag/ImportBundle/Import/PocketImport.php218
-rw-r--r--src/Wallabag/ImportBundle/Import/ReadabilityImport.php149
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagImport.php131
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV1Import.php15
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV2Import.php14
-rw-r--r--src/Wallabag/ImportBundle/Redis/Producer.php36
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/rabbit.yml30
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/redis.yml81
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/services.yml5
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig8
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Import/check_queue.html.twig11
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig12
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig2
-rw-r--r--src/Wallabag/UserBundle/Resources/config/services.yml6
50 files changed, 1056 insertions, 390 deletions
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index 143def4f..bd7b55f9 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -317,8 +317,13 @@ class InstallCommand extends ContainerAwareCommand
317 'section' => 'export', 317 'section' => 'export',
318 ], 318 ],
319 [ 319 [
320 'name' => 'pocket_consumer_key', 320 'name' => 'import_with_redis',
321 'value' => null, 321 'value' => '0',
322 'section' => 'import',
323 ],
324 [
325 'name' => 'import_with_rabbitmq',
326 'value' => '0',
322 'section' => 'import', 327 'section' => 'import',
323 ], 328 ],
324 [ 329 [
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 624576b5..40111af0 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -17,26 +17,35 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
17class EntryController extends Controller 17class EntryController extends Controller
18{ 18{
19 /** 19 /**
20 * @param Entry $entry 20 * Fetch content and update entry.
21 * In case it fails, entry will return to avod loosing the data.
22 *
23 * @param Entry $entry
24 * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded
25 *
26 * @return Entry
21 */ 27 */
22 private function updateEntry(Entry $entry) 28 private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved')
23 { 29 {
30 // put default title in case of fetching content failed
31 $entry->setTitle('No title found');
32
33 $message = 'flashes.entry.notice.'.$prefixMessage;
34
24 try { 35 try {
25 $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); 36 $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
26
27 $em = $this->getDoctrine()->getManager();
28 $em->persist($entry);
29 $em->flush();
30 } catch (\Exception $e) { 37 } catch (\Exception $e) {
31 $this->get('logger')->error('Error while saving an entry', [ 38 $this->get('logger')->error('Error while saving an entry', [
32 'exception' => $e, 39 'exception' => $e,
33 'entry' => $entry, 40 'entry' => $entry,
34 ]); 41 ]);
35 42
36 return false; 43 $message = 'flashes.entry.notice.'.$prefixMessage.'_failed';
37 } 44 }
38 45
39 return true; 46 $this->get('session')->getFlashBag()->add('notice', $message);
47
48 return $entry;
40 } 49 }
41 50
42 /** 51 /**
@@ -66,12 +75,11 @@ class EntryController extends Controller
66 return $this->redirect($this->generateUrl('view', ['id' => $existingEntry->getId()])); 75 return $this->redirect($this->generateUrl('view', ['id' => $existingEntry->getId()]));
67 } 76 }
68 77
69 $message = 'flashes.entry.notice.entry_saved'; 78 $this->updateEntry($entry);
70 if (false === $this->updateEntry($entry)) {
71 $message = 'flashes.entry.notice.entry_saved_failed';
72 }
73 79
74 $this->get('session')->getFlashBag()->add('notice', $message); 80 $em = $this->getDoctrine()->getManager();
81 $em->persist($entry);
82 $em->flush();
75 83
76 return $this->redirect($this->generateUrl('homepage')); 84 return $this->redirect($this->generateUrl('homepage'));
77 } 85 }
@@ -95,6 +103,10 @@ class EntryController extends Controller
95 103
96 if (false === $this->checkIfEntryAlreadyExists($entry)) { 104 if (false === $this->checkIfEntryAlreadyExists($entry)) {
97 $this->updateEntry($entry); 105 $this->updateEntry($entry);
106
107 $em = $this->getDoctrine()->getManager();
108 $em->persist($entry);
109 $em->flush();
98 } 110 }
99 111
100 return $this->redirect($this->generateUrl('homepage')); 112 return $this->redirect($this->generateUrl('homepage'));
@@ -316,15 +328,11 @@ class EntryController extends Controller
316 { 328 {
317 $this->checkUserAction($entry); 329 $this->checkUserAction($entry);
318 330
319 $message = 'flashes.entry.notice.entry_reloaded'; 331 $this->updateEntry($entry, 'entry_reloaded');
320 if (false === $this->updateEntry($entry)) {
321 $message = 'flashes.entry.notice.entry_reload_failed';
322 }
323 332
324 $this->get('session')->getFlashBag()->add( 333 $em = $this->getDoctrine()->getManager();
325 'notice', 334 $em->persist($entry);
326 $message 335 $em->flush();
327 );
328 336
329 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()])); 337 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
330 } 338 }
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
index 03be9667..921c739f 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
@@ -20,6 +20,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
20 $adminConfig->setItemsPerPage(30); 20 $adminConfig->setItemsPerPage(30);
21 $adminConfig->setReadingSpeed(1); 21 $adminConfig->setReadingSpeed(1);
22 $adminConfig->setLanguage('en'); 22 $adminConfig->setLanguage('en');
23 $adminConfig->setPocketConsumerKey('xxxxx');
23 24
24 $manager->persist($adminConfig); 25 $manager->persist($adminConfig);
25 26
@@ -30,6 +31,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
30 $bobConfig->setItemsPerPage(10); 31 $bobConfig->setItemsPerPage(10);
31 $bobConfig->setReadingSpeed(1); 32 $bobConfig->setReadingSpeed(1);
32 $bobConfig->setLanguage('fr'); 33 $bobConfig->setLanguage('fr');
34 $bobConfig->setPocketConsumerKey(null);
33 35
34 $manager->persist($bobConfig); 36 $manager->persist($bobConfig);
35 37
@@ -40,6 +42,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
40 $emptyConfig->setItemsPerPage(10); 42 $emptyConfig->setItemsPerPage(10);
41 $emptyConfig->setReadingSpeed(1); 43 $emptyConfig->setReadingSpeed(1);
42 $emptyConfig->setLanguage('en'); 44 $emptyConfig->setLanguage('en');
45 $emptyConfig->setPocketConsumerKey(null);
43 46
44 $manager->persist($emptyConfig); 47 $manager->persist($emptyConfig);
45 48
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
index b4309304..9425f961 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
@@ -91,8 +91,13 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
91 'section' => 'export', 91 'section' => 'export',
92 ], 92 ],
93 [ 93 [
94 'name' => 'pocket_consumer_key', 94 'name' => 'import_with_redis',
95 'value' => null, 95 'value' => '0',
96 'section' => 'import',
97 ],
98 [
99 'name' => 'import_with_rabbitmq',
100 'value' => '0',
96 'section' => 'import', 101 'section' => 'import',
97 ], 102 ],
98 [ 103 [
diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php
index a25656d3..d0f0e3f3 100644
--- a/src/Wallabag/CoreBundle/Entity/Config.php
+++ b/src/Wallabag/CoreBundle/Entity/Config.php
@@ -81,6 +81,13 @@ class Config
81 private $readingSpeed; 81 private $readingSpeed;
82 82
83 /** 83 /**
84 * @var string
85 *
86 * @ORM\Column(name="pocket_consumer_key", type="string", nullable=true)
87 */
88 private $pocketConsumerKey;
89
90 /**
84 * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config") 91 * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config")
85 */ 92 */
86 private $user; 93 private $user;
@@ -279,6 +286,30 @@ class Config
279 } 286 }
280 287
281 /** 288 /**
289 * Set pocketConsumerKey.
290 *
291 * @param string $pocketConsumerKey
292 *
293 * @return Config
294 */
295 public function setPocketConsumerKey($pocketConsumerKey)
296 {
297 $this->pocketConsumerKey = $pocketConsumerKey;
298
299 return $this;
300 }
301
302 /**
303 * Get pocketConsumerKey.
304 *
305 * @return string
306 */
307 public function getPocketConsumerKey()
308 {
309 return $this->pocketConsumerKey;
310 }
311
312 /**
282 * @param TaggingRule $rule 313 * @param TaggingRule $rule
283 * 314 *
284 * @return Config 315 * @return Config
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index c3e6b4d5..a4b0d7a8 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -97,7 +97,7 @@ class Entry
97 private $content; 97 private $content;
98 98
99 /** 99 /**
100 * @var date 100 * @var \DateTime
101 * 101 *
102 * @ORM\Column(name="created_at", type="datetime") 102 * @ORM\Column(name="created_at", type="datetime")
103 * 103 *
@@ -106,7 +106,7 @@ class Entry
106 private $createdAt; 106 private $createdAt;
107 107
108 /** 108 /**
109 * @var date 109 * @var \DateTime
110 * 110 *
111 * @ORM\Column(name="updated_at", type="datetime") 111 * @ORM\Column(name="updated_at", type="datetime")
112 * 112 *
@@ -410,7 +410,22 @@ class Entry
410 } 410 }
411 411
412 /** 412 /**
413 * @return string 413 * Set created_at.
414 * Only used when importing data from an other service.
415 *
416 * @param \DateTime $createdAt
417 *
418 * @return Entry
419 */
420 public function setCreatedAt(\DateTime $createdAt)
421 {
422 $this->createdAt = $createdAt;
423
424 return $this;
425 }
426
427 /**
428 * @return \DateTime
414 */ 429 */
415 public function getCreatedAt() 430 public function getCreatedAt()
416 { 431 {
@@ -418,7 +433,7 @@ class Entry
418 } 433 }
419 434
420 /** 435 /**
421 * @return string 436 * @return \DateTime
422 */ 437 */
423 public function getUpdatedAt() 438 public function getUpdatedAt()
424 { 439 {
diff --git a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
index 7d25cc80..0bac2874 100644
--- a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
@@ -52,6 +52,9 @@ class ConfigType extends AbstractType
52 'choices' => array_flip($this->languages), 52 'choices' => array_flip($this->languages),
53 'label' => 'config.form_settings.language_label', 53 'label' => 'config.form_settings.language_label',
54 ]) 54 ])
55 ->add('pocket_consumer_key', null, [
56 'label' => 'config.form_settings.pocket_consumer_key_label',
57 ])
55 ->add('save', SubmitType::class, [ 58 ->add('save', SubmitType::class, [
56 'label' => 'config.form.save', 59 'label' => 'config.form.save',
57 ]) 60 ])
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index e95ef452..23e6d3ca 100644
--- a/src/Wallabag/CoreBundle/Resources/config/services.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/services.yml
@@ -125,3 +125,11 @@ services:
125 arguments: 125 arguments:
126 - "@security.token_storage" 126 - "@security.token_storage"
127 - "@router" 127 - "@router"
128
129 wallabag_core.redis.client:
130 class: Predis\Client
131 arguments:
132 -
133 host: '%redis_host%'
134 port: '%redis_port%'
135 schema: tcp
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index 073dee28..9f051edb 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -68,6 +68,7 @@ config:
68 # 200_word: 'I read ~200 words per minute' 68 # 200_word: 'I read ~200 words per minute'
69 # 300_word: 'I read ~300 words per minute' 69 # 300_word: 'I read ~300 words per minute'
70 # 400_word: 'I read ~400 words per minute' 70 # 400_word: 'I read ~400 words per minute'
71 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer
71 form_rss: 72 form_rss:
72 description: 'RSS-feeds fra wallabag gør det muligt at læse de artikler, der gemmes i wallabag, med din RSS-læser. Det kræver, at du genererer et token først.' 73 description: 'RSS-feeds fra wallabag gør det muligt at læse de artikler, der gemmes i wallabag, med din RSS-læser. Det kræver, at du genererer et token først.'
73 token_label: 'RSS-Token' 74 token_label: 'RSS-Token'
@@ -346,6 +347,8 @@ import:
346 # page_title: 'Import > Readability' 347 # page_title: 'Import > Readability'
347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 # page_title: 'Developer' 354 # page_title: 'Developer'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 # entry_already_saved: 'Entry already saved on %date%' 415 # entry_already_saved: 'Entry already saved on %date%'
413 # entry_saved: 'Entry saved' 416 # entry_saved: 'Entry saved'
414 # entry_saved_failed: 'Failed to save entry' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 # entry_updated: 'Entry updated' 418 # entry_updated: 'Entry updated'
416 # entry_reloaded: 'Entry reloaded' 419 # entry_reloaded: 'Entry reloaded'
417 # entry_reload_failed: 'Failed to reload entry' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Artikel arkiveret' 421 entry_archived: 'Artikel arkiveret'
419 entry_unarchived: 'Artikel ikke længere arkiveret' 422 entry_unarchived: 'Artikel ikke længere arkiveret'
420 entry_starred: 'Artikel markeret som favorit' 423 entry_starred: 'Artikel markeret som favorit'
@@ -428,6 +431,7 @@ flashes:
428 # failed: 'Import failed, please try again.' 431 # failed: 'Import failed, please try again.'
429 # failed_on_file: 'Error while processing import. Please verify your import file.' 432 # failed_on_file: 'Error while processing import. Please verify your import file.'
430 # summary: 'Import summary: %imported% imported, %skipped% already saved.' 433 # summary: 'Import summary: %imported% imported, %skipped% already saved.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 # client_created: 'New client created.' 437 # client_created: 'New client created.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
index 4cfd240f..cbfacd55 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'Ich lese ~200 Wörter pro Minute' 68 200_word: 'Ich lese ~200 Wörter pro Minute'
69 300_word: 'Ich lese ~300 Wörter pro Minute' 69 300_word: 'Ich lese ~300 Wörter pro Minute'
70 400_word: 'Ich lese ~400 Wörter pro Minute' 70 400_word: 'Ich lese ~400 Wörter pro Minute'
71 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren
71 form_rss: 72 form_rss:
72 description: 'Die RSS-Feeds von wallabag erlauben es dir, deine gespeicherten Artikel mit deinem bevorzugten RSS-Reader zu lesen. Vorher musst du jedoch einen Token erstellen.' 73 description: 'Die RSS-Feeds von wallabag erlauben es dir, deine gespeicherten Artikel mit deinem bevorzugten RSS-Reader zu lesen. Vorher musst du jedoch einen Token erstellen.'
73 token_label: 'RSS-token' 74 token_label: 'RSS-token'
@@ -346,6 +347,8 @@ import:
346 page_title: 'Aus Readability importieren' 347 page_title: 'Aus Readability importieren'
347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 page_title: 'Entwickler' 354 page_title: 'Entwickler'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 entry_already_saved: 'Eintrag bereits am %date% gespeichert' 415 entry_already_saved: 'Eintrag bereits am %date% gespeichert'
413 entry_saved: 'Eintrag gespeichert' 416 entry_saved: 'Eintrag gespeichert'
414 # entry_saved_failed: 'Failed to save entry' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 entry_updated: 'Eintrag aktualisiert' 418 entry_updated: 'Eintrag aktualisiert'
416 entry_reloaded: 'Eintrag neugeladen' 419 entry_reloaded: 'Eintrag neugeladen'
417 entry_reload_failed: 'Neuladen des Eintrags fehlgeschlagen' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Artikel archiviert' 421 entry_archived: 'Artikel archiviert'
419 entry_unarchived: 'Artikel dearchiviert' 422 entry_unarchived: 'Artikel dearchiviert'
420 entry_starred: 'Artikel favorisiert' 423 entry_starred: 'Artikel favorisiert'
@@ -428,6 +431,7 @@ flashes:
428 failed: 'Import fehlgeschlagen, bitte erneut probieren.' 431 failed: 'Import fehlgeschlagen, bitte erneut probieren.'
429 failed_on_file: 'Fehler während des Imports. Bitte überprüfe deine Import-Datei.' 432 failed_on_file: 'Fehler während des Imports. Bitte überprüfe deine Import-Datei.'
430 summary: 'Import-Zusammenfassung: %imported% importiert, %skipped% bereits gespeichert.' 433 summary: 'Import-Zusammenfassung: %imported% importiert, %skipped% bereits gespeichert.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 client_created: 'Neuer Client erstellt.' 437 client_created: 'Neuer Client erstellt.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index 42374b40..21e2405c 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'I read ~200 words per minute' 68 200_word: 'I read ~200 words per minute'
69 300_word: 'I read ~300 words per minute' 69 300_word: 'I read ~300 words per minute'
70 400_word: 'I read ~400 words per minute' 70 400_word: 'I read ~400 words per minute'
71 pocket_consumer_key_label: Consumer key for Pocket to import contents
71 form_rss: 72 form_rss:
72 description: 'RSS feeds provided by wallabag allow you to read your saved articles with your favourite RSS reader. You need to generate a token first.' 73 description: 'RSS feeds provided by wallabag allow you to read your saved articles with your favourite RSS reader. You need to generate a token first.'
73 token_label: 'RSS token' 74 token_label: 'RSS token'
@@ -346,6 +347,8 @@ import:
346 page_title: 'Import > Readability' 347 page_title: 'Import > Readability'
347 description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 page_title: 'Developer' 354 page_title: 'Developer'
@@ -413,10 +416,10 @@ flashes:
413 notice: 416 notice:
414 entry_already_saved: 'Entry already saved on %date%' 417 entry_already_saved: 'Entry already saved on %date%'
415 entry_saved: 'Entry saved' 418 entry_saved: 'Entry saved'
416 entry_saved_failed: 'Failed to save entry' 419 entry_saved_failed: 'Entry saved but fetching content failed'
417 entry_updated: 'Entry updated' 420 entry_updated: 'Entry updated'
418 entry_reloaded: 'Entry reloaded' 421 entry_reloaded: 'Entry reloaded'
419 entry_reload_failed: 'Failed to reload entry' 422 entry_reload_failed: 'Entry reloaded but fetching content failed'
420 entry_archived: 'Entry archived' 423 entry_archived: 'Entry archived'
421 entry_unarchived: 'Entry unarchived' 424 entry_unarchived: 'Entry unarchived'
422 entry_starred: 'Entry starred' 425 entry_starred: 'Entry starred'
@@ -430,6 +433,7 @@ flashes:
430 failed: 'Import failed, please try again.' 433 failed: 'Import failed, please try again.'
431 failed_on_file: 'Error while processing import. Please verify your import file.' 434 failed_on_file: 'Error while processing import. Please verify your import file.'
432 summary: 'Import summary: %imported% imported, %skipped% already saved.' 435 summary: 'Import summary: %imported% imported, %skipped% already saved.'
436 summary_with_queue: 'Import summary: %queued% queued.'
433 developer: 437 developer:
434 notice: 438 notice:
435 client_created: 'New client %name% created.' 439 client_created: 'New client %name% created.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
index ee84cc62..43f376d4 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'Leo ~200 palabras por minuto' 68 200_word: 'Leo ~200 palabras por minuto'
69 300_word: 'Leo ~300 palabras por minuto' 69 300_word: 'Leo ~300 palabras por minuto'
70 400_word: 'Leo ~400 palabras por minuto' 70 400_word: 'Leo ~400 palabras por minuto'
71 # pocket_consumer_key_label: Consumer key for Pocket to import contents
71 form_rss: 72 form_rss:
72 description: 'Los feeds RSS de wallabag permiten leer los artículos guardados con su lector RSS favorito. Necesita generar un token primero' 73 description: 'Los feeds RSS de wallabag permiten leer los artículos guardados con su lector RSS favorito. Necesita generar un token primero'
73 token_label: 'RSS token' 74 token_label: 'RSS token'
@@ -346,6 +347,8 @@ import:
346 page_title: 'Importar > Readability' 347 page_title: 'Importar > Readability'
347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 page_title: 'Promotor' 354 page_title: 'Promotor'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 entry_already_saved: 'Entrada ya guardada por %fecha%' 415 entry_already_saved: 'Entrada ya guardada por %fecha%'
413 entry_saved: 'Entrada guardada' 416 entry_saved: 'Entrada guardada'
414 # entry_saved_failed: 'Failed to save entry' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 entry_updated: 'Entrada actualizada' 418 entry_updated: 'Entrada actualizada'
416 entry_reloaded: 'Entrada recargada' 419 entry_reloaded: 'Entrada recargada'
417 entry_reload_failed: 'Entrada recargada reprobada' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Artículo archivado' 421 entry_archived: 'Artículo archivado'
419 entry_unarchived: 'Artículo desarchivado' 422 entry_unarchived: 'Artículo desarchivado'
420 entry_starred: 'Artículo guardado en los favoritos' 423 entry_starred: 'Artículo guardado en los favoritos'
@@ -425,9 +428,10 @@ flashes:
425 tag_added: 'Etiqueta añadida' 428 tag_added: 'Etiqueta añadida'
426 import: 429 import:
427 notice: 430 notice:
428 failed: 'Importación reprobada, por favor inténtelo de nuevo.' 431 failed: 'Importación reprobada, por favor inténtelo de nuevo.'
429 failed_on_file: 'Se ocurre un error por procesar importación. Por favor verifique su archivo importado.' 432 failed_on_file: 'Se ocurre un error por procesar importación. Por favor verifique su archivo importado.'
430 summary: 'Resúmen importado: %importado% importado, %saltados% ya guardado.' 433 summary: 'Resúmen importado: %importado% importado, %saltados% ya guardado.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 client_created: 'Nuevo cliente creado.' 437 client_created: 'Nuevo cliente creado.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
index e9af1e8d..56418ef9 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'من تقریباً ۲۰۰ واژه را در دقیقه می‌خوانم' 68 200_word: 'من تقریباً ۲۰۰ واژه را در دقیقه می‌خوانم'
69 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم' 69 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم'
70 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم' 70 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم'
71 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب
71 form_rss: 72 form_rss:
72 description: 'با خوراک آر-اس-اس که wallabag در اختیارتان می‌گذارد، می‌توانید مقاله‌های ذخیره‌شده را در نرم‌افزار آر-اس-اس دلخواه خود بخوانید. برای این کار نخست باید یک کد بسازید.' 73 description: 'با خوراک آر-اس-اس که wallabag در اختیارتان می‌گذارد، می‌توانید مقاله‌های ذخیره‌شده را در نرم‌افزار آر-اس-اس دلخواه خود بخوانید. برای این کار نخست باید یک کد بسازید.'
73 token_label: 'کد آر-اس-اس' 74 token_label: 'کد آر-اس-اس'
@@ -344,8 +345,10 @@ import:
344 description: 'این برنامه همهٔ داده‌های شما را در نسخهٔ ۲ wallabag درون‌ریزی می‌کند. به بخش «همهٔ مقاله‌ها» بروید و در بخش «برون‌ریزی» روی "JSON" کلیک کنید. با این کار شما پرونده‌ای به شکل "All articles.json" دریافت خواهید کرد.' 345 description: 'این برنامه همهٔ داده‌های شما را در نسخهٔ ۲ wallabag درون‌ریزی می‌کند. به بخش «همهٔ مقاله‌ها» بروید و در بخش «برون‌ریزی» روی "JSON" کلیک کنید. با این کار شما پرونده‌ای به شکل "All articles.json" دریافت خواهید کرد.'
345 readability: 346 readability:
346 page_title: 'درون‌ریزی > Readability' 347 page_title: 'درون‌ریزی > Readability'
347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 # page_title: 'Developer' 354 # page_title: 'Developer'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' 415 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود'
413 entry_saved: 'مقاله ذخیره شد' 416 entry_saved: 'مقاله ذخیره شد'
414 # entry_saved_failed: 'Failed to save entry' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 entry_updated: 'مقاله به‌روز شد' 418 entry_updated: 'مقاله به‌روز شد'
416 entry_reloaded: 'مقاله به‌روز شد' 419 entry_reloaded: 'مقاله به‌روز شد'
417 entry_reload_failed: 'به‌روزرسانی مقاله شکست خورد' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'مقاله بایگانی شد' 421 entry_archived: 'مقاله بایگانی شد'
419 entry_unarchived: 'مقاله از بایگانی درآمد' 422 entry_unarchived: 'مقاله از بایگانی درآمد'
420 entry_starred: 'مقاله برگزیده شد' 423 entry_starred: 'مقاله برگزیده شد'
@@ -428,6 +431,7 @@ flashes:
428 failed: 'درون‌ریزی شکست خورد. لطفاً دوباره تلاش کنید.' 431 failed: 'درون‌ریزی شکست خورد. لطفاً دوباره تلاش کنید.'
429 failed_on_file: 'خطا هنگام پردازش پروندهٔ ورودی. آیا پروندهٔ درون‌ریزی شده سالم است؟' 432 failed_on_file: 'خطا هنگام پردازش پروندهٔ ورودی. آیا پروندهٔ درون‌ریزی شده سالم است؟'
430 summary: 'گزارش درون‌ریزی: %imported% وارد شد, %skipped% از قبل ذخیره شده بود.' 433 summary: 'گزارش درون‌ریزی: %imported% وارد شد, %skipped% از قبل ذخیره شده بود.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 # client_created: 'New client created.' 437 # client_created: 'New client created.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index 402cdf4a..bde21866 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'Je lis environ 200 mots par minute' 68 200_word: 'Je lis environ 200 mots par minute'
69 300_word: 'Je lis environ 300 mots par minute' 69 300_word: 'Je lis environ 300 mots par minute'
70 400_word: 'Je lis environ 400 mots par minute' 70 400_word: 'Je lis environ 400 mots par minute'
71 pocket_consumer_key_label: Clé d'authentification Pocket pour importer les données
71 form_rss: 72 form_rss:
72 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." 73 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."
73 token_label: 'Jeton RSS' 74 token_label: 'Jeton RSS'
@@ -346,6 +347,8 @@ import:
346 page_title: 'Importer > Readability' 347 page_title: 'Importer > Readability'
347 description: 'Cet outil va importer toutes vos données de Readability. Sur la page des outils (https://www.readability.com/tools/), cliquez sur "Export your data" dans la section "Data Export". Vous allez recevoir un email avec un lien pour télécharger le json.' 348 description: 'Cet outil va importer toutes vos données de Readability. Sur la page des outils (https://www.readability.com/tools/), cliquez sur "Export your data" dans la section "Data Export". Vous allez recevoir un email avec un lien pour télécharger le json.'
348 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l'importer." 349 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l'importer."
350 worker:
351 enabled: "Les imports sont asynchrones. Une fois l'import commencé un worker externe traitera les messages un par un. Le service activé est :"
349 352
350developer: 353developer:
351 page_title: 'Développeur' 354 page_title: 'Développeur'
@@ -413,10 +416,10 @@ flashes:
413 notice: 416 notice:
414 entry_already_saved: 'Article déjà sauvergardé le %date%' 417 entry_already_saved: 'Article déjà sauvergardé le %date%'
415 entry_saved: 'Article enregistré' 418 entry_saved: 'Article enregistré'
416 entry_saved_failed: "L'enregistrement a écho" 419 entry_saved_failed: 'Article enregistré mais impossible de récupérer le contenu'
417 entry_updated: 'Article mis à jour' 420 entry_updated: 'Article mis à jour'
418 entry_reloaded: 'Article rechargé' 421 entry_reloaded: 'Article rechargé'
419 entry_reload_failed: "Le rechargement de l'article a échoué" 422 entry_reload_failed: "Article mis à jour mais impossible de récupérer le contenu"
420 entry_archived: 'Article marqué comme lu' 423 entry_archived: 'Article marqué comme lu'
421 entry_unarchived: 'Article marqué comme non lu' 424 entry_unarchived: 'Article marqué comme non lu'
422 entry_starred: 'Article ajouté dans les favoris' 425 entry_starred: 'Article ajouté dans les favoris'
@@ -430,6 +433,7 @@ flashes:
430 failed: "L'import a échoué, veuillez ré-essayer" 433 failed: "L'import a échoué, veuillez ré-essayer"
431 failed_on_file: "Erreur lors du traitement de l'import. Vérifier votre fichier." 434 failed_on_file: "Erreur lors du traitement de l'import. Vérifier votre fichier."
432 summary: "Rapport d'import: %imported% importés, %skipped% déjà présent." 435 summary: "Rapport d'import: %imported% importés, %skipped% déjà présent."
436 summary_with_queue: "Rapport d'import: %queued% en cours de traitement."
433 developer: 437 developer:
434 notice: 438 notice:
435 client_created: 'Nouveau client %name% créé' 439 client_created: 'Nouveau client %name% créé'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
index 3aee4816..26bb31ba 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'Leggo ~200 parole al minuto' 68 200_word: 'Leggo ~200 parole al minuto'
69 300_word: 'Leggo ~300 parole al minuto' 69 300_word: 'Leggo ~300 parole al minuto'
70 400_word: 'Leggo ~400 parole al minuto' 70 400_word: 'Leggo ~400 parole al minuto'
71 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti
71 form_rss: 72 form_rss:
72 description: 'I feed RSS generati da wallabag ti permettono di leggere i tuoi contenuti salvati con il tuo lettore di RSS preferito. Prima, devi generare un token.' 73 description: 'I feed RSS generati da wallabag ti permettono di leggere i tuoi contenuti salvati con il tuo lettore di RSS preferito. Prima, devi generare un token.'
73 token_label: 'RSS token' 74 token_label: 'RSS token'
@@ -343,8 +344,10 @@ import:
343 description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v2. Vai in "Tutti i contenuti", e, nella sidebar di esportazione, clicca su "JSON". Otterrai un file "Tutti i contenuti.json".' 344 description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v2. Vai in "Tutti i contenuti", e, nella sidebar di esportazione, clicca su "JSON". Otterrai un file "Tutti i contenuti.json".'
344 readability: 345 readability:
345 page_title: 'Importa da > Readability' 346 page_title: 'Importa da > Readability'
346 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
347 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
349 worker:
350 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
348 351
349developer: 352developer:
350 page_title: 'Sviluppatori' 353 page_title: 'Sviluppatori'
@@ -410,10 +413,10 @@ flashes:
410 notice: 413 notice:
411 entry_already_saved: 'Contenuto già salvato in data %date%' 414 entry_already_saved: 'Contenuto già salvato in data %date%'
412 entry_saved: 'Contenuto salvato' 415 entry_saved: 'Contenuto salvato'
413 # entry_saved_failed: 'Failed to save entry' 416 # entry_saved_failed: 'Entry saved but fetching content failed'
414 entry_updated: 'Contenuto aggiornato' 417 entry_updated: 'Contenuto aggiornato'
415 entry_reloaded: 'Contenuto ricaricato' 418 entry_reloaded: 'Contenuto ricaricato'
416 entry_reload_failed: 'Errore nel ricaricamento del contenuto' 419 # entry_reload_failed: 'Entry reloaded but fetching content failed'
417 entry_archived: 'Contenuto archiviato' 420 entry_archived: 'Contenuto archiviato'
418 entry_unarchived: 'Contenuto dis-archiviato' 421 entry_unarchived: 'Contenuto dis-archiviato'
419 entry_starred: 'Contenuto segnato come preferito' 422 entry_starred: 'Contenuto segnato come preferito'
@@ -427,6 +430,7 @@ flashes:
427 failed: 'Importazione fallita, riprova.' 430 failed: 'Importazione fallita, riprova.'
428 failed_on_file: 'Errore durante la processazione dei dati da importare. Verifica il tuo file di import.' 431 failed_on_file: 'Errore durante la processazione dei dati da importare. Verifica il tuo file di import.'
429 summary: 'Sommario di importazione: %imported% importati, %skipped% già salvati.' 432 summary: 'Sommario di importazione: %imported% importati, %skipped% già salvati.'
433 # summary_with_queue: 'Import summary: %queued% queued.'
430 developer: 434 developer:
431 notice: 435 notice:
432 client_created: 'Nuovo client creato.' 436 client_created: 'Nuovo client creato.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
index 7d9d4e24..c6818449 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
@@ -68,6 +68,7 @@ config:
68 200_word: "Legissi a l'entorn de 200 mots per minuta" 68 200_word: "Legissi a l'entorn de 200 mots per minuta"
69 300_word: "Legissi a l'entorn de 300 mots per minuta" 69 300_word: "Legissi a l'entorn de 300 mots per minuta"
70 400_word: "Legissi a l'entorn de 400 mots per minuta" 70 400_word: "Legissi a l'entorn de 400 mots per minuta"
71 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas
71 form_rss: 72 form_rss:
72 description: "Los fluxes RSS fornits per wallabag vos permeton de legir vòstres articles salvagardats dins vòstre lector de fluxes preferit. Per los poder emplegar, vos cal, d'en primièr crear un geton." 73 description: "Los fluxes RSS fornits per wallabag vos permeton de legir vòstres articles salvagardats dins vòstre lector de fluxes preferit. Per los poder emplegar, vos cal, d'en primièr crear un geton."
73 token_label: 'Geton RSS' 74 token_label: 'Geton RSS'
@@ -346,6 +347,8 @@ import:
346 page_title: 'Importer > Readability' 347 page_title: 'Importer > Readability'
347 description: "Aquesta aisina importarà totas vòstres articles de Readability. Sus la pagina de l'aisina (https://www.readability.com/tools/), clicatz sus \"Export your data\" dins la seccion \"Data Export\". Recebretz un corrièl per telecargar un json (qu'acaba pas amb un .json de fach)." 348 description: "Aquesta aisina importarà totas vòstres articles de Readability. Sus la pagina de l'aisina (https://www.readability.com/tools/), clicatz sus \"Export your data\" dins la seccion \"Data Export\". Recebretz un corrièl per telecargar un json (qu'acaba pas amb un .json de fach)."
348 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar." 349 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar."
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 page_title: 'Desvolopador' 354 page_title: 'Desvolopador'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 entry_already_saved: 'Article ja salvargardat lo %date%' 415 entry_already_saved: 'Article ja salvargardat lo %date%'
413 entry_saved: 'Article enregistrat' 416 entry_saved: 'Article enregistrat'
414 entry_saved_failed: "Fracàs de l'enregistrament de l'entrada" 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 entry_updated: 'Article mes a jorn' 418 entry_updated: 'Article mes a jorn'
416 entry_reloaded: 'Article recargat' 419 entry_reloaded: 'Article recargat'
417 entry_reload_failed: "Fracàs de l'actualizacion de l'article" 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Article marcat coma legit' 421 entry_archived: 'Article marcat coma legit'
419 entry_unarchived: 'Article marcat coma pas legit' 422 entry_unarchived: 'Article marcat coma pas legit'
420 entry_starred: 'Article apondut dins los favorits' 423 entry_starred: 'Article apondut dins los favorits'
@@ -428,6 +431,7 @@ flashes:
428 failed: "L'importacion a fracassat, mercés de tornar ensajar" 431 failed: "L'importacion a fracassat, mercés de tornar ensajar"
429 failed_on_file: "Errorr pendent du tractament de l'import. Mercés de verificar vòstre fichièr." 432 failed_on_file: "Errorr pendent du tractament de l'import. Mercés de verificar vòstre fichièr."
430 summary: "Rapòrt d'import: %imported% importats, %skipped% ja presents." 433 summary: "Rapòrt d'import: %imported% importats, %skipped% ja presents."
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 client_created: 'Novèl client creat' 437 client_created: 'Novèl client creat'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index da50cd4c..fb821966 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -68,6 +68,7 @@ config:
68 200_word: 'Czytam ~200 słów na minutę' 68 200_word: 'Czytam ~200 słów na minutę'
69 300_word: 'Czytam ~300 słów na minutę' 69 300_word: 'Czytam ~300 słów na minutę'
70 400_word: 'Czytam ~400 słów na minutę' 70 400_word: 'Czytam ~400 słów na minutę'
71 pocket_consumer_key_label: Klucz klienta Pocket do importu zawartości
71 form_rss: 72 form_rss:
72 description: 'Kanały RSS prowadzone przez wallabag pozwalają Ci na czytanie twoich zapisanych artykułów w twoium ulubionym czytniku RSS. Musisz najpierw wynegenerować tokena.‌' 73 description: 'Kanały RSS prowadzone przez wallabag pozwalają Ci na czytanie twoich zapisanych artykułów w twoium ulubionym czytniku RSS. Musisz najpierw wynegenerować tokena.‌'
73 token_label: 'Token RSS' 74 token_label: 'Token RSS'
@@ -346,6 +347,8 @@ import:
346 page_title: 'Import > Readability' 347 page_title: 'Import > Readability'
347 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Readability. Na stronie narzędzi (https://www.readability.com/tools/), kliknij na "Export your data" w sekcji "Data Export". Otrzymach email z plikiem JSON (plik nie będzie zawierał rozszerzenia .json).' 348 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Readability. Na stronie narzędzi (https://www.readability.com/tools/), kliknij na "Export your data" w sekcji "Data Export". Otrzymach email z plikiem JSON (plik nie będzie zawierał rozszerzenia .json).'
348 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.' 349 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 page_title: 'Deweloper' 354 page_title: 'Deweloper'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 entry_already_saved: 'Wpis już został dodany %date%' 415 entry_already_saved: 'Wpis już został dodany %date%'
413 entry_saved: 'Wpis zapisany' 416 entry_saved: 'Wpis zapisany'
414 entry_saved_failed: 'Zapis artykułu się nie powiódł' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 entry_updated: 'Wpis zaktualizowany' 418 entry_updated: 'Wpis zaktualizowany'
416 entry_reloaded: 'Wpis ponownie załadowany' 419 entry_reloaded: 'Wpis ponownie załadowany'
417 entry_reload_failed: 'Błąd ponownego załadowania' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Wpis dodany do archiwum' 421 entry_archived: 'Wpis dodany do archiwum'
419 entry_unarchived: 'Wpis usunięty z archiwum' 422 entry_unarchived: 'Wpis usunięty z archiwum'
420 entry_starred: 'Wpis oznaczony gwiazdką' 423 entry_starred: 'Wpis oznaczony gwiazdką'
@@ -428,6 +431,7 @@ flashes:
428 failed: 'Nieudany import, prosimy spróbować ponownie.' 431 failed: 'Nieudany import, prosimy spróbować ponownie.'
429 failed_on_file: 'Błąd podczas ptrzetwarzania pliku. Sprawdż swój importowany plik.' 432 failed_on_file: 'Błąd podczas ptrzetwarzania pliku. Sprawdż swój importowany plik.'
430 summary: 'Podsumowanie importu: %imported% zaimportowane, %skipped% już zapisane.' 433 summary: 'Podsumowanie importu: %imported% zaimportowane, %skipped% już zapisane.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 client_created: 'Nowy klient utworzony.' 437 client_created: 'Nowy klient utworzony.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index f41609d0..3d22e29d 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -68,6 +68,7 @@ config:
68 # 200_word: 'I read ~200 words per minute' 68 # 200_word: 'I read ~200 words per minute'
69 # 300_word: 'I read ~300 words per minute' 69 # 300_word: 'I read ~300 words per minute'
70 # 400_word: 'I read ~400 words per minute' 70 # 400_word: 'I read ~400 words per minute'
71 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket
71 form_rss: 72 form_rss:
72 description: 'Feed-urile RSS oferite de wallabag îți permit să-ți citești articolele salvate în reader-ul tău preferat RSS.' 73 description: 'Feed-urile RSS oferite de wallabag îți permit să-ți citești articolele salvate în reader-ul tău preferat RSS.'
73 token_label: 'RSS-Token' 74 token_label: 'RSS-Token'
@@ -346,6 +347,8 @@ import:
346 # page_title: 'Import > Readability' 347 # page_title: 'Import > Readability'
347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 # page_title: 'Developer' 354 # page_title: 'Developer'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 # entry_already_saved: 'Entry already saved on %date%' 415 # entry_already_saved: 'Entry already saved on %date%'
413 # entry_saved: 'Entry saved' 416 # entry_saved: 'Entry saved'
414 # entry_saved_failed: 'Failed to save entry' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 # entry_updated: 'Entry updated' 418 # entry_updated: 'Entry updated'
416 # entry_reloaded: 'Entry reloaded' 419 # entry_reloaded: 'Entry reloaded'
417 # entry_reload_failed: 'Failed to reload entry' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Articol arhivat' 421 entry_archived: 'Articol arhivat'
419 entry_unarchived: 'Articol dezarhivat' 422 entry_unarchived: 'Articol dezarhivat'
420 entry_starred: 'Articol adăugat la favorite' 423 entry_starred: 'Articol adăugat la favorite'
@@ -428,6 +431,7 @@ flashes:
428 # failed: 'Import failed, please try again.' 431 # failed: 'Import failed, please try again.'
429 # failed_on_file: 'Error while processing import. Please verify your import file.' 432 # failed_on_file: 'Error while processing import. Please verify your import file.'
430 # summary: 'Import summary: %imported% imported, %skipped% already saved.' 433 # summary: 'Import summary: %imported% imported, %skipped% already saved.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 # client_created: 'New client created.' 437 # client_created: 'New client created.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
index 6dfbfa89..5099b002 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -68,6 +68,7 @@ config:
68 # 200_word: 'I read ~200 words per minute' 68 # 200_word: 'I read ~200 words per minute'
69 # 300_word: 'I read ~300 words per minute' 69 # 300_word: 'I read ~300 words per minute'
70 # 400_word: 'I read ~400 words per minute' 70 # 400_word: 'I read ~400 words per minute'
71 # pocket_consumer_key_label: Consumer key for Pocket to import contents
71 form_rss: 72 form_rss:
72 description: 'wallabag RSS akışı kaydetmiş olduğunuz makalelerini favori RSS okuyucunuzda görüntülemenizi sağlar. Bunu yapabilmek için öncelikle belirteç (token) oluşturmalısınız.' 73 description: 'wallabag RSS akışı kaydetmiş olduğunuz makalelerini favori RSS okuyucunuzda görüntülemenizi sağlar. Bunu yapabilmek için öncelikle belirteç (token) oluşturmalısınız.'
73 token_label: 'RSS belirteci (token)' 74 token_label: 'RSS belirteci (token)'
@@ -344,8 +345,10 @@ import:
344 # description: 'This importer will import all your wallabag v2 articles. Go to All articles, then, on the export sidebar, click on "JSON". You will have a "All articles.json" file.' 345 # description: 'This importer will import all your wallabag v2 articles. Go to All articles, then, on the export sidebar, click on "JSON". You will have a "All articles.json" file.'
345 readability: 346 readability:
346 page_title: 'İçe Aktar > Readability' 347 page_title: 'İçe Aktar > Readability'
347 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).' 348 # description: 'This importer will import all your Readability articles. On the tools (https://www.readability.com/tools/) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact).'
348 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 349 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
350 worker:
351 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
349 352
350developer: 353developer:
351 # page_title: 'Developer' 354 # page_title: 'Developer'
@@ -411,10 +414,10 @@ flashes:
411 notice: 414 notice:
412 entry_already_saved: 'Entry already saved on %date%' 415 entry_already_saved: 'Entry already saved on %date%'
413 entry_saved: 'Makale kaydedildi' 416 entry_saved: 'Makale kaydedildi'
414 # entry_saved_failed: 'Failed to save entry' 417 # entry_saved_failed: 'Entry saved but fetching content failed'
415 # entry_updated: 'Entry updated' 418 # entry_updated: 'Entry updated'
416 entry_reloaded: 'Makale içeriği yenilendi' 419 entry_reloaded: 'Makale içeriği yenilendi'
417 # entry_reload_failed: 'Failed to reload entry' 420 # entry_reload_failed: 'Entry reloaded but fetching content failed'
418 entry_archived: 'Makale arşivlendi' 421 entry_archived: 'Makale arşivlendi'
419 entry_unarchived: 'Makale arşivden çıkartıldı' 422 entry_unarchived: 'Makale arşivden çıkartıldı'
420 entry_starred: 'Makale favorilere eklendi' 423 entry_starred: 'Makale favorilere eklendi'
@@ -428,6 +431,7 @@ flashes:
428 # failed: 'Import failed, please try again.' 431 # failed: 'Import failed, please try again.'
429 # failed_on_file: 'Error while processing import. Please verify your import file.' 432 # failed_on_file: 'Error while processing import. Please verify your import file.'
430 # summary: 'Import summary: %imported% imported, %skipped% already saved.' 433 # summary: 'Import summary: %imported% imported, %skipped% already saved.'
434 # summary_with_queue: 'Import summary: %queued% queued.'
431 developer: 435 developer:
432 notice: 436 notice:
433 # client_created: 'New client created.' 437 # client_created: 'New client created.'
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
index f89265d4..6446cf2c 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
@@ -44,6 +44,18 @@
44 </div> 44 </div>
45 </fieldset> 45 </fieldset>
46 46
47 <fieldset class="w500p inline">
48 <div class="row">
49 {{ form_label(form.config.pocket_consumer_key) }}
50 {{ form_errors(form.config.pocket_consumer_key) }}
51 {{ form_widget(form.config.pocket_consumer_key) }}
52 <p>
53 &raquo;
54 <a href="https://getpocket.com/developer/docs/authentication">https://getpocket.com/developer/docs/authentication</a>
55 </p>
56 </div>
57 </fieldset>
58
47 {{ form_rest(form.config) }} 59 {{ form_rest(form.config) }}
48 </form> 60 </form>
49 61
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
index bf390e89..5330c353 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
@@ -62,6 +62,18 @@
62 </div> 62 </div>
63 </div> 63 </div>
64 64
65 <div class="row">
66 <div class="input-field col s12">
67 {{ form_label(form.config.pocket_consumer_key) }}
68 {{ form_errors(form.config.pocket_consumer_key) }}
69 {{ form_widget(form.config.pocket_consumer_key) }}
70 <p>
71 &raquo;
72 <a href="https://getpocket.com/developer/docs/authentication">https://getpocket.com/developer/docs/authentication</a>
73 </p>
74 </div>
75 </div>
76
65 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 77 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
66 {{ form_rest(form.config) }} 78 {{ form_rest(form.config) }}
67 </form> 79 </form>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
index 72becfa4..df05e2a4 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
@@ -19,6 +19,8 @@
19 Materialize.toast('{{ flashMessage|trans }}', 4000); 19 Materialize.toast('{{ flashMessage|trans }}', 4000);
20 </script> 20 </script>
21 {% endfor %} 21 {% endfor %}
22
23 {{ render(controller("WallabagImportBundle:Import:checkQueue")) }}
22{% endblock %} 24{% endblock %}
23 25
24{% block menu %} 26{% block menu %}
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php
index a4aa8531..20ecc6e1 100644
--- a/src/Wallabag/ImportBundle/Command/ImportCommand.php
+++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php
@@ -26,6 +26,10 @@ class ImportCommand extends ContainerAwareCommand
26 { 26 {
27 $output->writeln('Start : '.(new \DateTime())->format('d-m-Y G:i:s').' ---'); 27 $output->writeln('Start : '.(new \DateTime())->format('d-m-Y G:i:s').' ---');
28 28
29 if (!file_exists($input->getArgument('filepath'))) {
30 throw new Exception(sprintf('File "%s" not found', $input->getArgument('filepath')));
31 }
32
29 $em = $this->getContainer()->get('doctrine')->getManager(); 33 $em = $this->getContainer()->get('doctrine')->getManager();
30 // Turning off doctrine default logs queries for saving memory 34 // Turning off doctrine default logs queries for saving memory
31 $em->getConnection()->getConfiguration()->setSQLLogger(null); 35 $em->getConnection()->getConfiguration()->setSQLLogger(null);
@@ -43,9 +47,9 @@ class ImportCommand extends ContainerAwareCommand
43 } 47 }
44 48
45 $wallabag->setMarkAsRead($input->getOption('markAsRead')); 49 $wallabag->setMarkAsRead($input->getOption('markAsRead'));
50 $wallabag->setUser($user);
46 51
47 $res = $wallabag 52 $res = $wallabag
48 ->setUser($user)
49 ->setFilepath($input->getArgument('filepath')) 53 ->setFilepath($input->getArgument('filepath'))
50 ->import(); 54 ->import();
51 55
diff --git a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
new file mode 100644
index 00000000..5f90e00f
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
@@ -0,0 +1,44 @@
1<?php
2
3namespace Wallabag\ImportBundle\Command;
4
5use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
6use Symfony\Component\Config\Definition\Exception\Exception;
7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputOption;
9use Symfony\Component\Console\Input\InputInterface;
10use Symfony\Component\Console\Output\OutputInterface;
11use Simpleue\Worker\QueueWorker;
12
13class RedisWorkerCommand extends ContainerAwareCommand
14{
15 protected function configure()
16 {
17 $this
18 ->setName('wallabag:import:redis-worker')
19 ->setDescription('Launch Redis worker')
20 ->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket or readability')
21 ->addOption('maxIterations', '', InputOption::VALUE_OPTIONAL, 'Number of iterations before stoping', false)
22 ;
23 }
24
25 protected function execute(InputInterface $input, OutputInterface $output)
26 {
27 $output->writeln('Worker started at: '.(new \DateTime())->format('d-m-Y G:i:s'));
28 $output->writeln('Waiting for message ...');
29
30 $serviceName = $input->getArgument('serviceName');
31
32 if (!$this->getContainer()->has('wallabag_import.queue.redis.'.$serviceName) || !$this->getContainer()->has('wallabag_import.consumer.redis.'.$serviceName)) {
33 throw new Exception(sprintf('No queue or consumer found for service name: "%s"', $input->getArgument('serviceName')));
34 }
35
36 $worker = new QueueWorker(
37 $this->getContainer()->get('wallabag_import.queue.redis.'.$serviceName),
38 $this->getContainer()->get('wallabag_import.consumer.redis.'.$serviceName),
39 $input->getOption('maxIterations')
40 );
41
42 $worker->start();
43 }
44}
diff --git a/src/Wallabag/ImportBundle/Consumer/AMQPEntryConsumer.php b/src/Wallabag/ImportBundle/Consumer/AMQPEntryConsumer.php
new file mode 100644
index 00000000..5aaa8c03
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Consumer/AMQPEntryConsumer.php
@@ -0,0 +1,17 @@
1<?php
2
3namespace Wallabag\ImportBundle\Consumer;
4
5use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
6use PhpAmqpLib\Message\AMQPMessage;
7
8class AMQPEntryConsumer extends AbstractConsumer implements ConsumerInterface
9{
10 /**
11 * {@inheritdoc}
12 */
13 public function execute(AMQPMessage $msg)
14 {
15 return $this->handleMessage($msg->body);
16 }
17}
diff --git a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
new file mode 100644
index 00000000..2b85ad76
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
@@ -0,0 +1,74 @@
1<?php
2
3namespace Wallabag\ImportBundle\Consumer;
4
5use Doctrine\ORM\EntityManager;
6use Wallabag\ImportBundle\Import\AbstractImport;
7use Wallabag\UserBundle\Repository\UserRepository;
8use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Entity\Tag;
10use Psr\Log\LoggerInterface;
11use Psr\Log\NullLogger;
12
13abstract class AbstractConsumer
14{
15 protected $em;
16 protected $userRepository;
17 protected $import;
18 protected $logger;
19
20 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, LoggerInterface $logger = null)
21 {
22 $this->em = $em;
23 $this->userRepository = $userRepository;
24 $this->import = $import;
25 $this->logger = $logger ?: new NullLogger();
26 }
27
28 /**
29 * Handle a message and save it.
30 *
31 * @param string $body Message from the queue (in json)
32 *
33 * @return bool
34 */
35 protected function handleMessage($body)
36 {
37 $storedEntry = json_decode($body, true);
38
39 $user = $this->userRepository->find($storedEntry['userId']);
40
41 // no user? Drop message
42 if (null === $user) {
43 $this->logger->warning('Unable to retrieve user', ['entry' => $storedEntry]);
44
45 return false;
46 }
47
48 $this->import->setUser($user);
49
50 $entry = $this->import->parseEntry($storedEntry);
51
52 if (null === $entry) {
53 $this->logger->warning('Unable to parse entry', ['entry' => $storedEntry]);
54
55 return false;
56 }
57
58 try {
59 $this->em->flush();
60
61 // clear only affected entities
62 $this->em->clear(Entry::class);
63 $this->em->clear(Tag::class);
64 } catch (\Exception $e) {
65 $this->logger->warning('Unable to save entry', ['entry' => $storedEntry, 'exception' => $e]);
66
67 return false;
68 }
69
70 $this->logger->info('Content with url imported! ('.$entry->getUrl().')');
71
72 return true;
73 }
74}
diff --git a/src/Wallabag/ImportBundle/Consumer/RedisEntryConsumer.php b/src/Wallabag/ImportBundle/Consumer/RedisEntryConsumer.php
new file mode 100644
index 00000000..450b71ff
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Consumer/RedisEntryConsumer.php
@@ -0,0 +1,29 @@
1<?php
2
3namespace Wallabag\ImportBundle\Consumer;
4
5use Simpleue\Job\Job;
6
7class RedisEntryConsumer extends AbstractConsumer implements Job
8{
9 /**
10 * Handle one message by one message.
11 *
12 * @param string $job Content of the message (directly from Redis)
13 *
14 * @return bool
15 */
16 public function manage($job)
17 {
18 return $this->handleMessage($job);
19 }
20
21 /**
22 * Should tell if the given job will kill the worker.
23 * We don't want to stop it :).
24 */
25 public function isStopJob($job)
26 {
27 return false;
28 }
29}
diff --git a/src/Wallabag/ImportBundle/Controller/ImportController.php b/src/Wallabag/ImportBundle/Controller/ImportController.php
index c1486e38..ea4f7d7b 100644
--- a/src/Wallabag/ImportBundle/Controller/ImportController.php
+++ b/src/Wallabag/ImportBundle/Controller/ImportController.php
@@ -16,4 +16,66 @@ class ImportController extends Controller
16 'imports' => $this->get('wallabag_import.chain')->getAll(), 16 'imports' => $this->get('wallabag_import.chain')->getAll(),
17 ]); 17 ]);
18 } 18 }
19
20 /**
21 * Display how many messages are queue (both in Redis and RabbitMQ).
22 * Only for admins.
23 */
24 public function checkQueueAction()
25 {
26 $nbRedisMessages = null;
27 $nbRabbitMessages = null;
28
29 if (!$this->get('security.authorization_checker')->isGranted('ROLE_SUPER_ADMIN')) {
30 return $this->render('WallabagImportBundle:Import:check_queue.html.twig', [
31 'nbRedisMessages' => $nbRedisMessages,
32 'nbRabbitMessages' => $nbRabbitMessages,
33 ]);
34 }
35
36 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
37 $nbRabbitMessages = $this->getTotalMessageInRabbitQueue('pocket')
38 + $this->getTotalMessageInRabbitQueue('readability')
39 + $this->getTotalMessageInRabbitQueue('wallabag_v1')
40 + $this->getTotalMessageInRabbitQueue('wallabag_v2')
41 ;
42 } elseif ($this->get('craue_config')->get('import_with_redis')) {
43 $redis = $this->get('wallabag_core.redis.client');
44
45 $nbRedisMessages = $redis->llen('wallabag.import.pocket')
46 + $redis->llen('wallabag.import.readability')
47 + $redis->llen('wallabag.import.wallabag_v1')
48 + $redis->llen('wallabag.import.wallabag_v2')
49 ;
50 }
51
52 return $this->render('WallabagImportBundle:Import:check_queue.html.twig', [
53 'nbRedisMessages' => $nbRedisMessages,
54 'nbRabbitMessages' => $nbRabbitMessages,
55 ]);
56 }
57
58 /**
59 * Count message in RabbitMQ queue.
60 * It get one message without acking it (so it'll stay in the queue)
61 * which will include the total of *other* messages in the queue.
62 * Adding one to that messages will result in the full total message.
63 *
64 * @param string $importService The import service related: pocket, readability, wallabag_v1 or wallabag_v2
65 *
66 * @return int
67 */
68 private function getTotalMessageInRabbitQueue($importService)
69 {
70 $message = $this
71 ->get('old_sound_rabbit_mq.import_'.$importService.'_consumer')
72 ->getChannel()
73 ->basic_get('wallabag.import.'.$importService);
74
75 if (null === $message) {
76 return 0;
77 }
78
79 return $message->delivery_info['message_count'] + 1;
80 }
19} 81}
diff --git a/src/Wallabag/ImportBundle/Controller/PocketController.php b/src/Wallabag/ImportBundle/Controller/PocketController.php
index 36ee25bf..56be5cbf 100644
--- a/src/Wallabag/ImportBundle/Controller/PocketController.php
+++ b/src/Wallabag/ImportBundle/Controller/PocketController.php
@@ -11,11 +11,30 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
11class PocketController extends Controller 11class PocketController extends Controller
12{ 12{
13 /** 13 /**
14 * Return Pocket Import Service with or without RabbitMQ enabled.
15 *
16 * @return \Wallabag\ImportBundle\Import\PocketImport
17 */
18 private function getPocketImportService()
19 {
20 $pocket = $this->get('wallabag_import.pocket.import');
21 $pocket->setUser($this->getUser());
22
23 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
24 $pocket->setProducer($this->get('old_sound_rabbit_mq.import_pocket_producer'));
25 } elseif ($this->get('craue_config')->get('import_with_redis')) {
26 $pocket->setProducer($this->get('wallabag_import.producer.redis.pocket'));
27 }
28
29 return $pocket;
30 }
31
32 /**
14 * @Route("/pocket", name="import_pocket") 33 * @Route("/pocket", name="import_pocket")
15 */ 34 */
16 public function indexAction() 35 public function indexAction()
17 { 36 {
18 $pocket = $this->get('wallabag_import.pocket.import'); 37 $pocket = $this->getPocketImportService();
19 $form = $this->createFormBuilder($pocket) 38 $form = $this->createFormBuilder($pocket)
20 ->add('mark_as_read', CheckboxType::class, [ 39 ->add('mark_as_read', CheckboxType::class, [
21 'label' => 'import.form.mark_as_read_label', 40 'label' => 'import.form.mark_as_read_label',
@@ -24,8 +43,8 @@ class PocketController extends Controller
24 ->getForm(); 43 ->getForm();
25 44
26 return $this->render('WallabagImportBundle:Pocket:index.html.twig', [ 45 return $this->render('WallabagImportBundle:Pocket:index.html.twig', [
27 'import' => $this->get('wallabag_import.pocket.import'), 46 'import' => $this->getPocketImportService(),
28 'has_consumer_key' => '' == trim($this->get('craue_config')->get('pocket_consumer_key')) ? false : true, 47 'has_consumer_key' => '' === trim($this->getUser()->getConfig()->getPocketConsumerKey()) ? false : true,
29 'form' => $form->createView(), 48 'form' => $form->createView(),
30 ]); 49 ]);
31 } 50 }
@@ -35,7 +54,7 @@ class PocketController extends Controller
35 */ 54 */
36 public function authAction(Request $request) 55 public function authAction(Request $request)
37 { 56 {
38 $requestToken = $this->get('wallabag_import.pocket.import') 57 $requestToken = $this->getPocketImportService()
39 ->getRequestToken($this->generateUrl('import', [], UrlGeneratorInterface::ABSOLUTE_URL)); 58 ->getRequestToken($this->generateUrl('import', [], UrlGeneratorInterface::ABSOLUTE_URL));
40 59
41 if (false === $requestToken) { 60 if (false === $requestToken) {
@@ -62,7 +81,7 @@ class PocketController extends Controller
62 public function callbackAction() 81 public function callbackAction()
63 { 82 {
64 $message = 'flashes.import.notice.failed'; 83 $message = 'flashes.import.notice.failed';
65 $pocket = $this->get('wallabag_import.pocket.import'); 84 $pocket = $this->getPocketImportService();
66 85
67 $markAsRead = $this->get('session')->get('mark_as_read'); 86 $markAsRead = $this->get('session')->get('mark_as_read');
68 $this->get('session')->remove('mark_as_read'); 87 $this->get('session')->remove('mark_as_read');
@@ -83,6 +102,12 @@ class PocketController extends Controller
83 '%imported%' => $summary['imported'], 102 '%imported%' => $summary['imported'],
84 '%skipped%' => $summary['skipped'], 103 '%skipped%' => $summary['skipped'],
85 ]); 104 ]);
105
106 if (0 < $summary['queued']) {
107 $message = $this->get('translator')->trans('flashes.import.notice.summary_with_queue', [
108 '%queued%' => $summary['queued'],
109 ]);
110 }
86 } 111 }
87 112
88 $this->get('session')->getFlashBag()->add( 113 $this->get('session')->getFlashBag()->add(
diff --git a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
index b61aa99c..d00e22c2 100644
--- a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
+++ b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
@@ -18,15 +18,21 @@ class ReadabilityController extends Controller
18 $form->handleRequest($request); 18 $form->handleRequest($request);
19 19
20 $readability = $this->get('wallabag_import.readability.import'); 20 $readability = $this->get('wallabag_import.readability.import');
21 $readability->setUser($this->getUser());
22
23 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
24 $readability->setProducer($this->get('old_sound_rabbit_mq.import_readability_producer'));
25 } elseif ($this->get('craue_config')->get('import_with_redis')) {
26 $readability->setProducer($this->get('wallabag_import.producer.redis.readability'));
27 }
21 28
22 if ($form->isValid()) { 29 if ($form->isValid()) {
23 $file = $form->get('file')->getData(); 30 $file = $form->get('file')->getData();
24 $markAsRead = $form->get('mark_as_read')->getData(); 31 $markAsRead = $form->get('mark_as_read')->getData();
25 $name = 'readability_'.$this->getUser()->getId().'.json'; 32 $name = 'readability_'.$this->getUser()->getId().'.json';
26 33
27 if (in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { 34 if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
28 $res = $readability 35 $res = $readability
29 ->setUser($this->getUser())
30 ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) 36 ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
31 ->setMarkAsRead($markAsRead) 37 ->setMarkAsRead($markAsRead)
32 ->import(); 38 ->import();
@@ -40,6 +46,12 @@ class ReadabilityController extends Controller
40 '%skipped%' => $summary['skipped'], 46 '%skipped%' => $summary['skipped'],
41 ]); 47 ]);
42 48
49 if (0 < $summary['queued']) {
50 $message = $this->get('translator')->trans('flashes.import.notice.summary_with_queue', [
51 '%queued%' => $summary['queued'],
52 ]);
53 }
54
43 unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); 55 unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
44 } 56 }
45 57
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagController.php b/src/Wallabag/ImportBundle/Controller/WallabagController.php
index 76ced0d2..9c0cde80 100644
--- a/src/Wallabag/ImportBundle/Controller/WallabagController.php
+++ b/src/Wallabag/ImportBundle/Controller/WallabagController.php
@@ -38,15 +38,15 @@ abstract class WallabagController extends Controller
38 $form->handleRequest($request); 38 $form->handleRequest($request);
39 39
40 $wallabag = $this->getImportService(); 40 $wallabag = $this->getImportService();
41 $wallabag->setUser($this->getUser());
41 42
42 if ($form->isValid()) { 43 if ($form->isValid()) {
43 $file = $form->get('file')->getData(); 44 $file = $form->get('file')->getData();
44 $markAsRead = $form->get('mark_as_read')->getData(); 45 $markAsRead = $form->get('mark_as_read')->getData();
45 $name = $this->getUser()->getId().'.json'; 46 $name = $this->getUser()->getId().'.json';
46 47
47 if (in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) { 48 if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
48 $res = $wallabag 49 $res = $wallabag
49 ->setUser($this->getUser())
50 ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) 50 ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
51 ->setMarkAsRead($markAsRead) 51 ->setMarkAsRead($markAsRead)
52 ->import(); 52 ->import();
@@ -60,6 +60,12 @@ abstract class WallabagController extends Controller
60 '%skipped%' => $summary['skipped'], 60 '%skipped%' => $summary['skipped'],
61 ]); 61 ]);
62 62
63 if (0 < $summary['queued']) {
64 $message = $this->get('translator')->trans('flashes.import.notice.summary_with_queue', [
65 '%queued%' => $summary['queued'],
66 ]);
67 }
68
63 unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name); 69 unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
64 } 70 }
65 71
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php b/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php
index 3e748d57..312c7a35 100644
--- a/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php
+++ b/src/Wallabag/ImportBundle/Controller/WallabagV1Controller.php
@@ -12,7 +12,15 @@ class WallabagV1Controller extends WallabagController
12 */ 12 */
13 protected function getImportService() 13 protected function getImportService()
14 { 14 {
15 return $this->get('wallabag_import.wallabag_v1.import'); 15 $service = $this->get('wallabag_import.wallabag_v1.import');
16
17 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
18 $service->setProducer($this->get('old_sound_rabbit_mq.import_wallabag_v1_producer'));
19 } elseif ($this->get('craue_config')->get('import_with_redis')) {
20 $service->setProducer($this->get('wallabag_import.producer.redis.wallabag_v1'));
21 }
22
23 return $service;
16 } 24 }
17 25
18 /** 26 /**
diff --git a/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php b/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php
index c2a42165..45211fe6 100644
--- a/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php
+++ b/src/Wallabag/ImportBundle/Controller/WallabagV2Controller.php
@@ -12,7 +12,15 @@ class WallabagV2Controller extends WallabagController
12 */ 12 */
13 protected function getImportService() 13 protected function getImportService()
14 { 14 {
15 return $this->get('wallabag_import.wallabag_v2.import'); 15 $service = $this->get('wallabag_import.wallabag_v2.import');
16
17 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
18 $service->setProducer($this->get('old_sound_rabbit_mq.import_wallabag_v2_producer'));
19 } elseif ($this->get('craue_config')->get('import_with_redis')) {
20 $service->setProducer($this->get('wallabag_import.producer.redis.wallabag_v2'));
21 }
22
23 return $service;
16 } 24 }
17 25
18 /** 26 /**
diff --git a/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php b/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php
index 92a167d9..f50424c1 100644
--- a/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php
+++ b/src/Wallabag/ImportBundle/Form/Type/UploadImportType.php
@@ -15,6 +15,7 @@ class UploadImportType extends AbstractType
15 $builder 15 $builder
16 ->add('file', FileType::class, [ 16 ->add('file', FileType::class, [
17 'label' => 'import.form.file_label', 17 'label' => 'import.form.file_label',
18 'required' => true,
18 ]) 19 ])
19 ->add('mark_as_read', CheckboxType::class, [ 20 ->add('mark_as_read', CheckboxType::class, [
20 'label' => 'import.form.mark_as_read_label', 21 'label' => 'import.form.mark_as_read_label',
diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php
index 14377a35..a1a14576 100644
--- a/src/Wallabag/ImportBundle/Import/AbstractImport.php
+++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php
@@ -7,12 +7,21 @@ use Psr\Log\NullLogger;
7use Doctrine\ORM\EntityManager; 7use Doctrine\ORM\EntityManager;
8use Wallabag\CoreBundle\Helper\ContentProxy; 8use Wallabag\CoreBundle\Helper\ContentProxy;
9use Wallabag\CoreBundle\Entity\Entry; 9use Wallabag\CoreBundle\Entity\Entry;
10use Wallabag\CoreBundle\Entity\Tag;
11use Wallabag\UserBundle\Entity\User;
12use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
10 13
11abstract class AbstractImport implements ImportInterface 14abstract class AbstractImport implements ImportInterface
12{ 15{
13 protected $em; 16 protected $em;
14 protected $logger; 17 protected $logger;
15 protected $contentProxy; 18 protected $contentProxy;
19 protected $producer;
20 protected $user;
21 protected $markAsRead;
22 protected $skippedEntries = 0;
23 protected $importedEntries = 0;
24 protected $queuedEntries = 0;
16 25
17 public function __construct(EntityManager $em, ContentProxy $contentProxy) 26 public function __construct(EntityManager $em, ContentProxy $contentProxy)
18 { 27 {
@@ -27,21 +36,150 @@ abstract class AbstractImport implements ImportInterface
27 } 36 }
28 37
29 /** 38 /**
39 * Set RabbitMQ/Redis Producer to send each entry to a queue.
40 * This method should be called when user has enabled RabbitMQ.
41 *
42 * @param ProducerInterface $producer
43 */
44 public function setProducer(ProducerInterface $producer)
45 {
46 $this->producer = $producer;
47 }
48
49 /**
50 * Set current user.
51 * Could the current *connected* user or one retrieve by the consumer.
52 *
53 * @param User $user
54 */
55 public function setUser(User $user)
56 {
57 $this->user = $user;
58 }
59
60 /**
61 * Set whether articles must be all marked as read.
62 *
63 * @param bool $markAsRead
64 */
65 public function setMarkAsRead($markAsRead)
66 {
67 $this->markAsRead = $markAsRead;
68
69 return $this;
70 }
71
72 /**
73 * Get whether articles must be all marked as read.
74 */
75 public function getMarkAsRead()
76 {
77 return $this->markAsRead;
78 }
79
80 /**
30 * Fetch content from the ContentProxy (using graby). 81 * Fetch content from the ContentProxy (using graby).
31 * If it fails return false instead of the updated entry. 82 * If it fails return the given entry to be saved in all case (to avoid user to loose the content).
32 * 83 *
33 * @param Entry $entry Entry to update 84 * @param Entry $entry Entry to update
34 * @param string $url Url to grab content for 85 * @param string $url Url to grab content for
35 * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url 86 * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url
36 * 87 *
37 * @return Entry|false 88 * @return Entry
38 */ 89 */
39 protected function fetchContent(Entry $entry, $url, array $content = []) 90 protected function fetchContent(Entry $entry, $url, array $content = [])
40 { 91 {
41 try { 92 try {
42 return $this->contentProxy->updateEntry($entry, $url, $content); 93 return $this->contentProxy->updateEntry($entry, $url, $content);
43 } catch (\Exception $e) { 94 } catch (\Exception $e) {
44 return false; 95 return $entry;
96 }
97 }
98
99 /**
100 * Parse and insert all given entries.
101 *
102 * @param $entries
103 */
104 protected function parseEntries($entries)
105 {
106 $i = 1;
107
108 foreach ($entries as $importedEntry) {
109 $entry = $this->parseEntry($importedEntry);
110
111 if (null === $entry) {
112 continue;
113 }
114
115 // flush every 20 entries
116 if (($i % 20) === 0) {
117 $this->em->flush();
118
119 // clear only affected entities
120 $this->em->clear(Entry::class);
121 $this->em->clear(Tag::class);
122 }
123 ++$i;
124 }
125
126 $this->em->flush();
127 }
128
129 /**
130 * Parse entries and send them to the queue.
131 * It should just be a simple loop on all item, no call to the database should be done
132 * to speedup queuing.
133 *
134 * Faster parse entries for Producer.
135 * We don't care to make check at this time. They'll be done by the consumer.
136 *
137 * @param array $entries
138 */
139 protected function parseEntriesForProducer(array $entries)
140 {
141 foreach ($entries as $importedEntry) {
142 // set userId for the producer (it won't know which user is connected)
143 $importedEntry['userId'] = $this->user->getId();
144
145 if ($this->markAsRead) {
146 $importedEntry = $this->setEntryAsRead($importedEntry);
147 }
148
149 ++$this->queuedEntries;
150
151 $this->producer->publish(json_encode($importedEntry));
45 } 152 }
46 } 153 }
154
155 /**
156 * {@inheritdoc}
157 */
158 public function getSummary()
159 {
160 return [
161 'skipped' => $this->skippedEntries,
162 'imported' => $this->importedEntries,
163 'queued' => $this->queuedEntries,
164 ];
165 }
166
167 /**
168 * Parse one entry.
169 *
170 * @param array $importedEntry
171 *
172 * @return Entry
173 */
174 abstract public function parseEntry(array $importedEntry);
175
176 /**
177 * Set current imported entry to archived / read.
178 * Implementation is different accross all imports.
179 *
180 * @param array $importedEntry
181 *
182 * @return array
183 */
184 abstract protected function setEntryAsRead(array $importedEntry);
47} 185}
diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php
index a6f905b1..e00eb44b 100644
--- a/src/Wallabag/ImportBundle/Import/PocketImport.php
+++ b/src/Wallabag/ImportBundle/Import/PocketImport.php
@@ -6,31 +6,34 @@ use Psr\Log\NullLogger;
6use Doctrine\ORM\EntityManager; 6use Doctrine\ORM\EntityManager;
7use GuzzleHttp\Client; 7use GuzzleHttp\Client;
8use GuzzleHttp\Exception\RequestException; 8use GuzzleHttp\Exception\RequestException;
9use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
10use Wallabag\CoreBundle\Entity\Entry; 9use Wallabag\CoreBundle\Entity\Entry;
11use Wallabag\CoreBundle\Helper\ContentProxy; 10use Wallabag\CoreBundle\Helper\ContentProxy;
12use Craue\ConfigBundle\Util\Config;
13 11
14class PocketImport extends AbstractImport 12class PocketImport extends AbstractImport
15{ 13{
16 private $user;
17 private $client; 14 private $client;
18 private $consumerKey; 15 private $accessToken;
19 private $skippedEntries = 0;
20 private $importedEntries = 0;
21 private $markAsRead;
22 protected $accessToken;
23 16
24 public function __construct(TokenStorageInterface $tokenStorage, EntityManager $em, ContentProxy $contentProxy, Config $craueConfig) 17 const NB_ELEMENTS = 5000;
18
19 public function __construct(EntityManager $em, ContentProxy $contentProxy)
25 { 20 {
26 $this->user = $tokenStorage->getToken()->getUser();
27 $this->em = $em; 21 $this->em = $em;
28 $this->contentProxy = $contentProxy; 22 $this->contentProxy = $contentProxy;
29 $this->consumerKey = $craueConfig->get('pocket_consumer_key');
30 $this->logger = new NullLogger(); 23 $this->logger = new NullLogger();
31 } 24 }
32 25
33 /** 26 /**
27 * Only used for test purpose.
28 *
29 * @return string
30 */
31 public function getAccessToken()
32 {
33 return $this->accessToken;
34 }
35
36 /**
34 * {@inheritdoc} 37 * {@inheritdoc}
35 */ 38 */
36 public function getName() 39 public function getName()
@@ -66,7 +69,7 @@ class PocketImport extends AbstractImport
66 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/request', 69 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/request',
67 [ 70 [
68 'body' => json_encode([ 71 'body' => json_encode([
69 'consumer_key' => $this->consumerKey, 72 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
70 'redirect_uri' => $redirectUri, 73 'redirect_uri' => $redirectUri,
71 ]), 74 ]),
72 ] 75 ]
@@ -96,7 +99,7 @@ class PocketImport extends AbstractImport
96 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/authorize', 99 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/authorize',
97 [ 100 [
98 'body' => json_encode([ 101 'body' => json_encode([
99 'consumer_key' => $this->consumerKey, 102 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
100 'code' => $code, 103 'code' => $code,
101 ]), 104 ]),
102 ] 105 ]
@@ -116,38 +119,22 @@ class PocketImport extends AbstractImport
116 } 119 }
117 120
118 /** 121 /**
119 * Set whether articles must be all marked as read.
120 *
121 * @param bool $markAsRead
122 */
123 public function setMarkAsRead($markAsRead)
124 {
125 $this->markAsRead = $markAsRead;
126
127 return $this;
128 }
129
130 /**
131 * Get whether articles must be all marked as read.
132 */
133 public function getMarkAsRead()
134 {
135 return $this->markAsRead;
136 }
137
138 /**
139 * {@inheritdoc} 122 * {@inheritdoc}
140 */ 123 */
141 public function import() 124 public function import($offset = 0)
142 { 125 {
126 static $run = 0;
127
143 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/get', 128 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/get',
144 [ 129 [
145 'body' => json_encode([ 130 'body' => json_encode([
146 'consumer_key' => $this->consumerKey, 131 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
147 'access_token' => $this->accessToken, 132 'access_token' => $this->accessToken,
148 'detailType' => 'complete', 133 'detailType' => 'complete',
149 'state' => 'all', 134 'state' => 'all',
150 'sort' => 'oldest', 135 'sort' => 'newest',
136 'count' => self::NB_ELEMENTS,
137 'offset' => $offset,
151 ]), 138 ]),
152 ] 139 ]
153 ); 140 );
@@ -162,20 +149,24 @@ class PocketImport extends AbstractImport
162 149
163 $entries = $response->json(); 150 $entries = $response->json();
164 151
165 $this->parseEntries($entries['list']); 152 if ($this->producer) {
153 $this->parseEntriesForProducer($entries['list']);
154 } else {
155 $this->parseEntries($entries['list']);
156 }
166 157
167 return true; 158 // if we retrieve exactly the amount of items requested it means we can get more
168 } 159 // re-call import and offset item by the amount previous received:
160 // - first call get 5k offset 0
161 // - second call get 5k offset 5k
162 // - and so on
163 if (count($entries['list']) === self::NB_ELEMENTS) {
164 ++$run;
169 165
170 /** 166 return $this->import(self::NB_ELEMENTS * $run);
171 * {@inheritdoc} 167 }
172 */ 168
173 public function getSummary() 169 return true;
174 {
175 return [
176 'skipped' => $this->skippedEntries,
177 'imported' => $this->importedEntries,
178 ];
179 } 170 }
180 171
181 /** 172 /**
@@ -189,77 +180,74 @@ class PocketImport extends AbstractImport
189 } 180 }
190 181
191 /** 182 /**
192 * @see https://getpocket.com/developer/docs/v3/retrieve 183 * {@inheritdoc}
193 * 184 *
194 * @param $entries 185 * @see https://getpocket.com/developer/docs/v3/retrieve
195 */ 186 */
196 private function parseEntries($entries) 187 public function parseEntry(array $importedEntry)
197 { 188 {
198 $i = 1; 189 $url = isset($importedEntry['resolved_url']) && $importedEntry['resolved_url'] != '' ? $importedEntry['resolved_url'] : $importedEntry['given_url'];
199 190
200 foreach ($entries as $pocketEntry) { 191 $existingEntry = $this->em
201 $url = isset($pocketEntry['resolved_url']) && $pocketEntry['resolved_url'] != '' ? $pocketEntry['resolved_url'] : $pocketEntry['given_url']; 192 ->getRepository('WallabagCoreBundle:Entry')
202 193 ->findByUrlAndUserId($url, $this->user->getId());
203 $existingEntry = $this->em 194
204 ->getRepository('WallabagCoreBundle:Entry') 195 if (false !== $existingEntry) {
205 ->findByUrlAndUserId($url, $this->user->getId()); 196 ++$this->skippedEntries;
206 197
207 if (false !== $existingEntry) { 198 return;
208 ++$this->skippedEntries; 199 }
209 continue; 200
210 } 201 $entry = new Entry($this->user);
211 202 $entry->setUrl($url);
212 $entry = new Entry($this->user); 203
213 $entry = $this->fetchContent($entry, $url); 204 // update entry with content (in case fetching failed, the given entry will be return)
214 205 $entry = $this->fetchContent($entry, $url);
215 // jump to next entry in case of problem while getting content 206
216 if (false === $entry) { 207 // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted
217 ++$this->skippedEntries; 208 $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead);
218 continue; 209
219 } 210 // 0 or 1 - 1 If the item is starred
220 211 $entry->setStarred($importedEntry['favorite'] == 1);
221 // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted 212
222 if ($pocketEntry['status'] == 1 || $this->markAsRead) { 213 $title = 'Untitled';
223 $entry->setArchived(true); 214 if (isset($importedEntry['resolved_title']) && $importedEntry['resolved_title'] != '') {
224 } 215 $title = $importedEntry['resolved_title'];
225 216 } elseif (isset($importedEntry['given_title']) && $importedEntry['given_title'] != '') {
226 // 0 or 1 - 1 If the item is starred 217 $title = $importedEntry['given_title'];
227 if ($pocketEntry['favorite'] == 1) { 218 }
228 $entry->setStarred(true); 219
229 } 220 $entry->setTitle($title);
230 221
231 $title = 'Untitled'; 222 // 0, 1, or 2 - 1 if the item has images in it - 2 if the item is an image
232 if (isset($pocketEntry['resolved_title']) && $pocketEntry['resolved_title'] != '') { 223 if (isset($importedEntry['has_image']) && $importedEntry['has_image'] > 0 && isset($importedEntry['images'][1])) {
233 $title = $pocketEntry['resolved_title']; 224 $entry->setPreviewPicture($importedEntry['images'][1]['src']);
234 } elseif (isset($pocketEntry['given_title']) && $pocketEntry['given_title'] != '') { 225 }
235 $title = $pocketEntry['given_title']; 226
236 } 227 if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) {
237 228 $this->contentProxy->assignTagsToEntry(
238 $entry->setTitle($title); 229 $entry,
239 230 array_keys($importedEntry['tags'])
240 // 0, 1, or 2 - 1 if the item has images in it - 2 if the item is an image 231 );
241 if (isset($pocketEntry['has_image']) && $pocketEntry['has_image'] > 0 && isset($pocketEntry['images'][1])) {
242 $entry->setPreviewPicture($pocketEntry['images'][1]['src']);
243 }
244
245 if (isset($pocketEntry['tags']) && !empty($pocketEntry['tags'])) {
246 $this->contentProxy->assignTagsToEntry(
247 $entry,
248 array_keys($pocketEntry['tags'])
249 );
250 }
251
252 $this->em->persist($entry);
253 ++$this->importedEntries;
254
255 // flush every 20 entries
256 if (($i % 20) === 0) {
257 $this->em->flush();
258 }
259 ++$i;
260 } 232 }
261 233
262 $this->em->flush(); 234 if (!empty($importedEntry['time_added'])) {
263 $this->em->clear(); 235 $entry->setCreatedAt((new \DateTime())->setTimestamp($importedEntry['time_added']));
236 }
237
238 $this->em->persist($entry);
239 ++$this->importedEntries;
240
241 return $entry;
242 }
243
244 /**
245 * {@inheritdoc}
246 */
247 protected function setEntryAsRead(array $importedEntry)
248 {
249 $importedEntry['status'] = '1';
250
251 return $importedEntry;
264 } 252 }
265} 253}
diff --git a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
index c7cfe15d..fa2b7053 100644
--- a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
+++ b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
@@ -3,28 +3,10 @@
3namespace Wallabag\ImportBundle\Import; 3namespace Wallabag\ImportBundle\Import;
4 4
5use Wallabag\CoreBundle\Entity\Entry; 5use Wallabag\CoreBundle\Entity\Entry;
6use Wallabag\UserBundle\Entity\User;
7 6
8class ReadabilityImport extends AbstractImport 7class ReadabilityImport extends AbstractImport
9{ 8{
10 private $user;
11 private $skippedEntries = 0;
12 private $importedEntries = 0;
13 private $filepath; 9 private $filepath;
14 private $markAsRead;
15
16 /**
17 * We define the user in a custom call because on the import command there is no logged in user.
18 * So we can't retrieve user from the `security.token_storage` service.
19 *
20 * @param User $user
21 */
22 public function setUser(User $user)
23 {
24 $this->user = $user;
25
26 return $this;
27 }
28 10
29 /** 11 /**
30 * {@inheritdoc} 12 * {@inheritdoc}
@@ -63,37 +45,6 @@ class ReadabilityImport extends AbstractImport
63 } 45 }
64 46
65 /** 47 /**
66 * Set whether articles must be all marked as read.
67 *
68 * @param bool $markAsRead
69 */
70 public function setMarkAsRead($markAsRead)
71 {
72 $this->markAsRead = $markAsRead;
73
74 return $this;
75 }
76
77 /**
78 * Get whether articles must be all marked as read.
79 */
80 public function getMarkAsRead()
81 {
82 return $this->markAsRead;
83 }
84
85 /**
86 * {@inheritdoc}
87 */
88 public function getSummary()
89 {
90 return [
91 'skipped' => $this->skippedEntries,
92 'imported' => $this->importedEntries,
93 ];
94 }
95
96 /**
97 * {@inheritdoc} 48 * {@inheritdoc}
98 */ 49 */
99 public function import() 50 public function import()
@@ -116,64 +67,66 @@ class ReadabilityImport extends AbstractImport
116 return false; 67 return false;
117 } 68 }
118 69
70 if ($this->producer) {
71 $this->parseEntriesForProducer($data['bookmarks']);
72
73 return true;
74 }
75
119 $this->parseEntries($data['bookmarks']); 76 $this->parseEntries($data['bookmarks']);
120 77
121 return true; 78 return true;
122 } 79 }
123 80
124 /** 81 /**
125 * Parse and insert all given entries. 82 * {@inheritdoc}
126 *
127 * @param $entries
128 */ 83 */
129 protected function parseEntries($entries) 84 public function parseEntry(array $importedEntry)
130 { 85 {
131 $i = 1; 86 $existingEntry = $this->em
132 87 ->getRepository('WallabagCoreBundle:Entry')
133 foreach ($entries as $importedEntry) { 88 ->findByUrlAndUserId($importedEntry['article__url'], $this->user->getId());
134 $existingEntry = $this->em 89
135 ->getRepository('WallabagCoreBundle:Entry') 90 if (false !== $existingEntry) {
136 ->findByUrlAndUserId($importedEntry['article__url'], $this->user->getId()); 91 ++$this->skippedEntries;
137 92
138 if (false !== $existingEntry) { 93 return;
139 ++$this->skippedEntries;
140 continue;
141 }
142
143 $data = [
144 'title' => $importedEntry['article__title'],
145 'url' => $importedEntry['article__url'],
146 'content_type' => '',
147 'language' => '',
148 'is_archived' => $importedEntry['archive'] || $this->markAsRead,
149 'is_starred' => $importedEntry['favorite'],
150 ];
151
152 $entry = $this->fetchContent(
153 new Entry($this->user),
154 $data['url'],
155 $data
156 );
157
158 // jump to next entry in case of problem while getting content
159 if (false === $entry) {
160 ++$this->skippedEntries;
161 continue;
162 }
163 $entry->setArchived($data['is_archived']);
164 $entry->setStarred($data['is_starred']);
165
166 $this->em->persist($entry);
167 ++$this->importedEntries;
168
169 // flush every 20 entries
170 if (($i % 20) === 0) {
171 $this->em->flush();
172 }
173 ++$i;
174 } 94 }
175 95
176 $this->em->flush(); 96 $data = [
177 $this->em->clear(); 97 'title' => $importedEntry['article__title'],
98 'url' => $importedEntry['article__url'],
99 'content_type' => '',
100 'language' => '',
101 'is_archived' => $importedEntry['archive'] || $this->markAsRead,
102 'is_starred' => $importedEntry['favorite'],
103 'created_at' => $importedEntry['date_added'],
104 ];
105
106 $entry = new Entry($this->user);
107 $entry->setUrl($data['url']);
108 $entry->setTitle($data['title']);
109
110 // update entry with content (in case fetching failed, the given entry will be return)
111 $entry = $this->fetchContent($entry, $data['url'], $data);
112
113 $entry->setArchived($data['is_archived']);
114 $entry->setStarred($data['is_starred']);
115 $entry->setCreatedAt(new \DateTime($data['created_at']));
116
117 $this->em->persist($entry);
118 ++$this->importedEntries;
119
120 return $entry;
121 }
122
123 /**
124 * {@inheritdoc}
125 */
126 protected function setEntryAsRead(array $importedEntry)
127 {
128 $importedEntry['archive'] = 1;
129
130 return $importedEntry;
178 } 131 }
179} 132}
diff --git a/src/Wallabag/ImportBundle/Import/WallabagImport.php b/src/Wallabag/ImportBundle/Import/WallabagImport.php
index 581ec178..043bb0a2 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagImport.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagImport.php
@@ -3,15 +3,10 @@
3namespace Wallabag\ImportBundle\Import; 3namespace Wallabag\ImportBundle\Import;
4 4
5use Wallabag\CoreBundle\Entity\Entry; 5use Wallabag\CoreBundle\Entity\Entry;
6use Wallabag\UserBundle\Entity\User;
7 6
8abstract class WallabagImport extends AbstractImport 7abstract class WallabagImport extends AbstractImport
9{ 8{
10 protected $user;
11 protected $skippedEntries = 0;
12 protected $importedEntries = 0;
13 protected $filepath; 9 protected $filepath;
14 protected $markAsRead;
15 // untitled in all languages from v1 10 // untitled in all languages from v1
16 protected $untitled = [ 11 protected $untitled = [
17 'Untitled', 12 'Untitled',
@@ -29,19 +24,6 @@ abstract class WallabagImport extends AbstractImport
29 ]; 24 ];
30 25
31 /** 26 /**
32 * We define the user in a custom call because on the import command there is no logged in user.
33 * So we can't retrieve user from the `security.token_storage` service.
34 *
35 * @param User $user
36 */
37 public function setUser(User $user)
38 {
39 $this->user = $user;
40
41 return $this;
42 }
43
44 /**
45 * {@inheritdoc} 27 * {@inheritdoc}
46 */ 28 */
47 abstract public function getName(); 29 abstract public function getName();
@@ -79,23 +61,18 @@ abstract class WallabagImport extends AbstractImport
79 return false; 61 return false;
80 } 62 }
81 63
64 if ($this->producer) {
65 $this->parseEntriesForProducer($data);
66
67 return true;
68 }
69
82 $this->parseEntries($data); 70 $this->parseEntries($data);
83 71
84 return true; 72 return true;
85 } 73 }
86 74
87 /** 75 /**
88 * {@inheritdoc}
89 */
90 public function getSummary()
91 {
92 return [
93 'skipped' => $this->skippedEntries,
94 'imported' => $this->importedEntries,
95 ];
96 }
97
98 /**
99 * Set file path to the json file. 76 * Set file path to the json file.
100 * 77 *
101 * @param string $filepath 78 * @param string $filepath
@@ -108,85 +85,59 @@ abstract class WallabagImport extends AbstractImport
108 } 85 }
109 86
110 /** 87 /**
111 * Set whether articles must be all marked as read. 88 * {@inheritdoc}
112 *
113 * @param bool $markAsRead
114 */ 89 */
115 public function setMarkAsRead($markAsRead) 90 public function parseEntry(array $importedEntry)
116 { 91 {
117 $this->markAsRead = $markAsRead; 92 $existingEntry = $this->em
93 ->getRepository('WallabagCoreBundle:Entry')
94 ->findByUrlAndUserId($importedEntry['url'], $this->user->getId());
118 95
119 return $this; 96 if (false !== $existingEntry) {
120 } 97 ++$this->skippedEntries;
121 98
122 /** 99 return;
123 * Parse and insert all given entries. 100 }
124 *
125 * @param $entries
126 */
127 protected function parseEntries($entries)
128 {
129 $i = 1;
130 101
131 foreach ($entries as $importedEntry) { 102 $data = $this->prepareEntry($importedEntry);
132 $existingEntry = $this->em
133 ->getRepository('WallabagCoreBundle:Entry')
134 ->findByUrlAndUserId($importedEntry['url'], $this->user->getId());
135 103
136 if (false !== $existingEntry) { 104 $entry = new Entry($this->user);
137 ++$this->skippedEntries; 105 $entry->setUrl($data['url']);
138 continue; 106 $entry->setTitle($data['title']);
139 }
140 107
141 $data = $this->prepareEntry($importedEntry, $this->markAsRead); 108 // update entry with content (in case fetching failed, the given entry will be return)
109 $entry = $this->fetchContent($entry, $data['url'], $data);
142 110
143 $entry = $this->fetchContent( 111 if (array_key_exists('tags', $data)) {
144 new Entry($this->user), 112 $this->contentProxy->assignTagsToEntry(
145 $importedEntry['url'], 113 $entry,
146 $data 114 $data['tags']
147 ); 115 );
116 }
148 117
149 // jump to next entry in case of problem while getting content 118 if (isset($importedEntry['preview_picture'])) {
150 if (false === $entry) { 119 $entry->setPreviewPicture($importedEntry['preview_picture']);
151 ++$this->skippedEntries;
152 continue;
153 }
154
155 if (array_key_exists('tags', $data)) {
156 $this->contentProxy->assignTagsToEntry(
157 $entry,
158 $data['tags']
159 );
160 }
161
162 if (isset($importedEntry['preview_picture'])) {
163 $entry->setPreviewPicture($importedEntry['preview_picture']);
164 }
165
166 $entry->setArchived($data['is_archived']);
167 $entry->setStarred($data['is_starred']);
168
169 $this->em->persist($entry);
170 ++$this->importedEntries;
171
172 // flush every 20 entries
173 if (($i % 20) === 0) {
174 $this->em->flush();
175 }
176 ++$i;
177 } 120 }
178 121
179 $this->em->flush(); 122 $entry->setArchived($data['is_archived']);
180 $this->em->clear(); 123 $entry->setStarred($data['is_starred']);
124
125 if (!empty($data['created_at'])) {
126 $entry->setCreatedAt(new \DateTime($data['created_at']));
127 }
128
129 $this->em->persist($entry);
130 ++$this->importedEntries;
131
132 return $entry;
181 } 133 }
182 134
183 /** 135 /**
184 * This should return a cleaned array for a given entry to be given to `updateEntry`. 136 * This should return a cleaned array for a given entry to be given to `updateEntry`.
185 * 137 *
186 * @param array $entry Data from the imported file 138 * @param array $entry Data from the imported file
187 * @param bool $markAsRead Should we mark as read content?
188 * 139 *
189 * @return array 140 * @return array
190 */ 141 */
191 abstract protected function prepareEntry($entry = [], $markAsRead = false); 142 abstract protected function prepareEntry($entry = []);
192} 143}
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
index 6cf3467a..4f001062 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php
@@ -31,7 +31,7 @@ class WallabagV1Import extends WallabagImport
31 /** 31 /**
32 * {@inheritdoc} 32 * {@inheritdoc}
33 */ 33 */
34 protected function prepareEntry($entry = [], $markAsRead = false) 34 protected function prepareEntry($entry = [])
35 { 35 {
36 $data = [ 36 $data = [
37 'title' => $entry['title'], 37 'title' => $entry['title'],
@@ -39,9 +39,10 @@ class WallabagV1Import extends WallabagImport
39 'url' => $entry['url'], 39 'url' => $entry['url'],
40 'content_type' => '', 40 'content_type' => '',
41 'language' => '', 41 'language' => '',
42 'is_archived' => $entry['is_read'] || $markAsRead, 42 'is_archived' => $entry['is_read'] || $this->markAsRead,
43 'is_starred' => $entry['is_fav'], 43 'is_starred' => $entry['is_fav'],
44 'tags' => '', 44 'tags' => '',
45 'created_at' => '',
45 ]; 46 ];
46 47
47 // force content to be refreshed in case on bad fetch in the v1 installation 48 // force content to be refreshed in case on bad fetch in the v1 installation
@@ -56,4 +57,14 @@ class WallabagV1Import extends WallabagImport
56 57
57 return $data; 58 return $data;
58 } 59 }
60
61 /**
62 * {@inheritdoc}
63 */
64 protected function setEntryAsRead(array $importedEntry)
65 {
66 $importedEntry['is_read'] = 1;
67
68 return $importedEntry;
69 }
59} 70}
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
index d0035b63..37c8ca14 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
@@ -31,12 +31,22 @@ class WallabagV2Import extends WallabagImport
31 /** 31 /**
32 * {@inheritdoc} 32 * {@inheritdoc}
33 */ 33 */
34 protected function prepareEntry($entry = [], $markAsRead = false) 34 protected function prepareEntry($entry = [])
35 { 35 {
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' => ($entry['is_archived'] || $markAsRead), 39 'is_archived' => ($entry['is_archived'] || $this->markAsRead),
40 ] + $entry; 40 ] + $entry;
41 } 41 }
42
43 /**
44 * {@inheritdoc}
45 */
46 protected function setEntryAsRead(array $importedEntry)
47 {
48 $importedEntry['is_archived'] = 1;
49
50 return $importedEntry;
51 }
42} 52}
diff --git a/src/Wallabag/ImportBundle/Redis/Producer.php b/src/Wallabag/ImportBundle/Redis/Producer.php
new file mode 100644
index 00000000..fedc3e57
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Redis/Producer.php
@@ -0,0 +1,36 @@
1<?php
2
3namespace Wallabag\ImportBundle\Redis;
4
5use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
6use Simpleue\Queue\RedisQueue;
7
8/**
9 * This is a proxy class for "Simpleue\Queue\RedisQueue".
10 * It allow us to use the same way to publish a message between RabbitMQ & Redis: publish().
11 *
12 * It implements the ProducerInterface of RabbitMQ (yes it's ugly) so we can have the same
13 * kind of class which implements the same interface.
14 * So we can inject either a RabbitMQ producer or a Redis producer with the same signature
15 */
16class Producer implements ProducerInterface
17{
18 private $queue;
19
20 public function __construct(RedisQueue $queue)
21 {
22 $this->queue = $queue;
23 }
24
25 /**
26 * Publish a message in the Redis queue.
27 *
28 * @param string $msgBody
29 * @param string $routingKey NOT USED
30 * @param array $additionalProperties NOT USED
31 */
32 public function publish($msgBody, $routingKey = '', $additionalProperties = array())
33 {
34 $this->queue->sendJob($msgBody);
35 }
36}
diff --git a/src/Wallabag/ImportBundle/Resources/config/rabbit.yml b/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
new file mode 100644
index 00000000..aa049749
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
@@ -0,0 +1,30 @@
1# RabbitMQ stuff
2services:
3 wallabag_import.consumer.amqp.pocket:
4 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
5 arguments:
6 - "@doctrine.orm.entity_manager"
7 - "@wallabag_user.user_repository"
8 - "@wallabag_import.pocket.import"
9 - "@logger"
10 wallabag_import.consumer.amqp.readability:
11 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
12 arguments:
13 - "@doctrine.orm.entity_manager"
14 - "@wallabag_user.user_repository"
15 - "@wallabag_import.readability.import"
16 - "@logger"
17 wallabag_import.consumer.amqp.wallabag_v1:
18 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
19 arguments:
20 - "@doctrine.orm.entity_manager"
21 - "@wallabag_user.user_repository"
22 - "@wallabag_import.wallabag_v1.import"
23 - "@logger"
24 wallabag_import.consumer.amqp.wallabag_v2:
25 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
26 arguments:
27 - "@doctrine.orm.entity_manager"
28 - "@wallabag_user.user_repository"
29 - "@wallabag_import.wallabag_v2.import"
30 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/redis.yml b/src/Wallabag/ImportBundle/Resources/config/redis.yml
new file mode 100644
index 00000000..7d3248e5
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Resources/config/redis.yml
@@ -0,0 +1,81 @@
1# Redis stuff
2services:
3 # readability
4 wallabag_import.queue.redis.readability:
5 class: Simpleue\Queue\RedisQueue
6 arguments:
7 - "@wallabag_core.redis.client"
8 - "wallabag.import.readability"
9
10 wallabag_import.producer.redis.readability:
11 class: Wallabag\ImportBundle\Redis\Producer
12 arguments:
13 - "@wallabag_import.queue.redis.readability"
14
15 wallabag_import.consumer.redis.readability:
16 class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
17 arguments:
18 - "@doctrine.orm.entity_manager"
19 - "@wallabag_user.user_repository"
20 - "@wallabag_import.readability.import"
21 - "@logger"
22
23 # pocket
24 wallabag_import.queue.redis.pocket:
25 class: Simpleue\Queue\RedisQueue
26 arguments:
27 - "@wallabag_core.redis.client"
28 - "wallabag.import.pocket"
29
30 wallabag_import.producer.redis.pocket:
31 class: Wallabag\ImportBundle\Redis\Producer
32 arguments:
33 - "@wallabag_import.queue.redis.pocket"
34
35 wallabag_import.consumer.redis.pocket:
36 class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
37 arguments:
38 - "@doctrine.orm.entity_manager"
39 - "@wallabag_user.user_repository"
40 - "@wallabag_import.pocket.import"
41 - "@logger"
42
43 # wallabag v1
44 wallabag_import.queue.redis.wallabag_v1:
45 class: Simpleue\Queue\RedisQueue
46 arguments:
47 - "@wallabag_core.redis.client"
48 - "wallabag.import.wallabag_v1"
49
50 wallabag_import.producer.redis.wallabag_v1:
51 class: Wallabag\ImportBundle\Redis\Producer
52 arguments:
53 - "@wallabag_import.queue.redis.wallabag_v1"
54
55 wallabag_import.consumer.redis.wallabag_v1:
56 class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
57 arguments:
58 - "@doctrine.orm.entity_manager"
59 - "@wallabag_user.user_repository"
60 - "@wallabag_import.wallabag_v1.import"
61 - "@logger"
62
63 # wallabag v2
64 wallabag_import.queue.redis.wallabag_v2:
65 class: Simpleue\Queue\RedisQueue
66 arguments:
67 - "@wallabag_core.redis.client"
68 - "wallabag.import.wallabag_v2"
69
70 wallabag_import.producer.redis.wallabag_v2:
71 class: Wallabag\ImportBundle\Redis\Producer
72 arguments:
73 - "@wallabag_import.queue.redis.wallabag_v2"
74
75 wallabag_import.consumer.redis.wallabag_v2:
76 class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
77 arguments:
78 - "@doctrine.orm.entity_manager"
79 - "@wallabag_user.user_repository"
80 - "@wallabag_import.wallabag_v2.import"
81 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml
index 520d43af..f03404ae 100644
--- a/src/Wallabag/ImportBundle/Resources/config/services.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/services.yml
@@ -1,3 +1,7 @@
1imports:
2 - { resource: rabbit.yml }
3 - { resource: redis.yml }
4
1services: 5services:
2 wallabag_import.chain: 6 wallabag_import.chain:
3 class: Wallabag\ImportBundle\Import\ImportChain 7 class: Wallabag\ImportBundle\Import\ImportChain
@@ -14,7 +18,6 @@ services:
14 wallabag_import.pocket.import: 18 wallabag_import.pocket.import:
15 class: Wallabag\ImportBundle\Import\PocketImport 19 class: Wallabag\ImportBundle\Import\PocketImport
16 arguments: 20 arguments:
17 - "@security.token_storage"
18 - "@doctrine.orm.entity_manager" 21 - "@doctrine.orm.entity_manager"
19 - "@wallabag_core.content_proxy" 22 - "@wallabag_core.content_proxy"
20 - "@craue_config" 23 - "@craue_config"
diff --git a/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig b/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig
new file mode 100644
index 00000000..2390a41f
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig
@@ -0,0 +1,8 @@
1{% set redis = craue_setting('import_with_redis') %}
2{% set rabbit = craue_setting('import_with_rabbitmq') %}
3
4{% if redis or rabbit %}
5 <div class="card-panel yellow darken-1 black-text">
6 {{ 'import.worker.enabled'|trans }} <strong>{% if rabbit %}RabbitMQ{% elseif redis %}Redis{% endif %}</strong>
7 </div>
8{% endif %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Import/check_queue.html.twig b/src/Wallabag/ImportBundle/Resources/views/Import/check_queue.html.twig
new file mode 100644
index 00000000..7168ea35
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Resources/views/Import/check_queue.html.twig
@@ -0,0 +1,11 @@
1{% if nbRedisMessages > 0 %}
2 <script>
3 Materialize.toast('Messages in queue: {{ nbRedisMessages }}', 4000);
4 </script>
5{% endif %}
6
7{% if nbRabbitMessages > 0 %}
8 <script>
9 Materialize.toast('Messages in queue: {{ nbRabbitMessages }}', 4000);
10 </script>
11{% endif %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
index 401dbd33..6195fa07 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
@@ -6,15 +6,13 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %}
10
9 {% if not has_consumer_key %} 11 {% if not has_consumer_key %}
10 <div class="card-panel red darken-1"> 12 <div class="card-panel red white-text">
11 {{ 'import.pocket.config_missing.description'|trans }} 13 {{ 'import.pocket.config_missing.description'|trans }}
12 14
13 {% if is_granted('ROLE_SUPER_ADMIN') %} 15 {{ 'import.pocket.config_missing.admin_message'|trans({'%keyurls%': '<a href="' ~ path('config') ~ '">', '%keyurle%':'</a>'})|raw }}
14 {{ 'import.pocket.config_missing.admin_message'|trans({'%keyurls%': '<a href="' ~ path('craue_config_settings_modify') ~ '#set-import">', '%keyurle%':'</a>'})|raw }}
15 {% else %}
16 {{ 'import.pocket.config_missing.user_message'|trans }}
17 {% endif %}
18 </div> 16 </div>
19 {% endif %} 17 {% endif %}
20 18
@@ -29,7 +27,7 @@
29 {{ form_label(form.mark_as_read) }} 27 {{ form_label(form.mark_as_read) }}
30 </div> 28 </div>
31 </div> 29 </div>
32 <button class="btn waves-effect waves-light" type="submit" name="action"> 30 <button class="btn waves-effect waves-light" type="submit" name="action" {% if not has_consumer_key %}disabled="disabled"{% endif %}>
33 {{ 'import.pocket.connect_to_pocket'|trans }} 31 {{ 'import.pocket.connect_to_pocket'|trans }}
34 </button> 32 </button>
35 </form> 33 </form>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
index f527d309..74653b0f 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %}
10
9 <div class="row"> 11 <div class="row">
10 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
11 <p>{{ 'import.readability.how_to'|trans }}</p> 13 <p>{{ 'import.readability.how_to'|trans }}</p>
diff --git a/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
index 13e24c8c..0b19bc34 100644
--- a/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %}
10
9 <div class="row"> 11 <div class="row">
10 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
11 <p>{{ 'import.wallabag_v1.how_to'|trans }}</p> 13 <p>{{ 'import.wallabag_v1.how_to'|trans }}</p>
diff --git a/src/Wallabag/UserBundle/Resources/config/services.yml b/src/Wallabag/UserBundle/Resources/config/services.yml
index d79d8fa2..05830555 100644
--- a/src/Wallabag/UserBundle/Resources/config/services.yml
+++ b/src/Wallabag/UserBundle/Resources/config/services.yml
@@ -14,3 +14,9 @@ services:
14 - "@router" 14 - "@router"
15 tags: 15 tags:
16 - { name: kernel.event_subscriber } 16 - { name: kernel.event_subscriber }
17
18 wallabag_user.user_repository:
19 class: Wallabag\UserBundle\Repository\UserRepository
20 factory: [ "@doctrine.orm.default_entity_manager", getRepository ]
21 arguments:
22 - WallabagUserBundle:User