mirror of
https://github.com/wallabag/wallabag.git
synced 2025-09-15 18:57:05 +00:00
Add Pocket CSV import
This commit is contained in:
parent
52a16bb75f
commit
c1397f43ac
17 changed files with 754 additions and 4 deletions
|
@ -18,6 +18,7 @@ use Wallabag\ImportBundle\Import\FirefoxImport;
|
|||
use Wallabag\ImportBundle\Import\InstapaperImport;
|
||||
use Wallabag\ImportBundle\Import\OmnivoreImport;
|
||||
use Wallabag\ImportBundle\Import\PinboardImport;
|
||||
use Wallabag\ImportBundle\Import\PocketCsvImport;
|
||||
use Wallabag\ImportBundle\Import\PocketHtmlImport;
|
||||
use Wallabag\ImportBundle\Import\ReadabilityImport;
|
||||
use Wallabag\ImportBundle\Import\ShaarliImport;
|
||||
|
@ -43,6 +44,7 @@ class ImportCommand extends Command
|
|||
private ElcuratorImport $elcuratorImport;
|
||||
private ShaarliImport $shaarliImport;
|
||||
private PocketHtmlImport $pocketHtmlImport;
|
||||
private PocketCsvImport $pocketCsvImport;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
|
@ -59,7 +61,8 @@ class ImportCommand extends Command
|
|||
ElcuratorImport $elcuratorImport,
|
||||
OmnivoreImport $omnivoreImport,
|
||||
ShaarliImport $shaarliImport,
|
||||
PocketHtmlImport $pocketHtmlImport
|
||||
PocketHtmlImport $pocketHtmlImport,
|
||||
PocketCsvImport $pocketCsvImport
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
|
@ -76,6 +79,7 @@ class ImportCommand extends Command
|
|||
$this->elcuratorImport = $elcuratorImport;
|
||||
$this->shaarliImport = $shaarliImport;
|
||||
$this->pocketHtmlImport = $pocketHtmlImport;
|
||||
$this->pocketCsvImport = $pocketCsvImport;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
@ -87,7 +91,7 @@ class ImportCommand extends Command
|
|||
->setDescription('Import entries from a JSON export')
|
||||
->addArgument('username', InputArgument::REQUIRED, 'User to populate')
|
||||
->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file')
|
||||
->addOption('importer', null, InputOption::VALUE_OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, delicious, readability, firefox, chrome, elcurator, shaarli or pocket', 'v1')
|
||||
->addOption('importer', null, InputOption::VALUE_OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, delicious, readability, firefox, chrome, elcurator, shaarli, pocket or pocket_csv', 'v1')
|
||||
->addOption('markAsRead', null, InputOption::VALUE_OPTIONAL, 'Mark all entries as read', false)
|
||||
->addOption('useUserId', null, InputOption::VALUE_NONE, 'Use user id instead of username to find account')
|
||||
->addOption('disableContentUpdate', null, InputOption::VALUE_NONE, 'Disable fetching updated content from URL')
|
||||
|
@ -159,6 +163,9 @@ class ImportCommand extends Command
|
|||
case 'pocket':
|
||||
$import = $this->pocketHtmlImport;
|
||||
break;
|
||||
case 'pocket_csv':
|
||||
$import = $this->pocketCsvImport;
|
||||
break;
|
||||
default:
|
||||
$import = $this->wallabagV1Import;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ class RabbitMQConsumerTotalProxy
|
|||
private Consumer $omnivoreConsumer;
|
||||
private Consumer $shaarliConsumer;
|
||||
private Consumer $pocketHtmlConsumer;
|
||||
private Consumer $pocketCsvConsumer;
|
||||
|
||||
public function __construct(
|
||||
Consumer $pocketConsumer,
|
||||
|
@ -37,7 +38,8 @@ class RabbitMQConsumerTotalProxy
|
|||
Consumer $elcuratorConsumer,
|
||||
Consumer $omnivoreConsumer,
|
||||
Consumer $shaarliConsumer,
|
||||
Consumer $pocketHtmlConsumer
|
||||
Consumer $pocketHtmlConsumer,
|
||||
Consumer $pocketCsvConsumer
|
||||
) {
|
||||
$this->pocketConsumer = $pocketConsumer;
|
||||
$this->readabilityConsumer = $readabilityConsumer;
|
||||
|
@ -52,6 +54,7 @@ class RabbitMQConsumerTotalProxy
|
|||
$this->omnivoreConsumer = $omnivoreConsumer;
|
||||
$this->shaarliConsumer = $shaarliConsumer;
|
||||
$this->pocketHtmlConsumer = $pocketHtmlConsumer;
|
||||
$this->pocketCsvConsumer = $pocketCsvConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,6 +108,9 @@ class RabbitMQConsumerTotalProxy
|
|||
case 'pocket_html':
|
||||
$consumer = $this->pocketHtmlConsumer;
|
||||
break;
|
||||
case 'pocket_csv':
|
||||
$consumer = $this->pocketCsvConsumer;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ class ImportController extends AbstractController
|
|||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('omnivore')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('shaarli')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('pocket_html')
|
||||
+ $this->rabbitMQConsumerTotalProxy->getTotalMessage('pocket_csv')
|
||||
;
|
||||
} catch (\Exception $e) {
|
||||
$rabbitNotInstalled = true;
|
||||
|
@ -81,6 +82,7 @@ class ImportController extends AbstractController
|
|||
+ $redis->llen('wallabag.import.omnivore')
|
||||
+ $redis->llen('wallabag.import.shaarli')
|
||||
+ $redis->llen('wallabag.import.pocket_html')
|
||||
+ $redis->llen('wallabag.import.pocket_csv')
|
||||
;
|
||||
} catch (\Exception $e) {
|
||||
$redisNotInstalled = true;
|
||||
|
|
57
src/Wallabag/ImportBundle/Controller/PocketCsvController.php
Normal file
57
src/Wallabag/ImportBundle/Controller/PocketCsvController.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Controller;
|
||||
|
||||
use Craue\ConfigBundle\Util\Config;
|
||||
use OldSound\RabbitMqBundle\RabbitMq\Producer as RabbitMqProducer;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Wallabag\ImportBundle\Import\PocketCsvImport;
|
||||
use Wallabag\ImportBundle\Redis\Producer as RedisProducer;
|
||||
|
||||
class PocketCsvController extends HtmlController
|
||||
{
|
||||
private PocketCsvImport $pocketCsvImport;
|
||||
private Config $craueConfig;
|
||||
private RabbitMqProducer $rabbitMqProducer;
|
||||
private RedisProducer $redisProducer;
|
||||
|
||||
public function __construct(PocketCsvImport $pocketCsvImport, Config $craueConfig, RabbitMqProducer $rabbitMqProducer, RedisProducer $redisProducer)
|
||||
{
|
||||
$this->pocketCsvImport = $pocketCsvImport;
|
||||
$this->craueConfig = $craueConfig;
|
||||
$this->rabbitMqProducer = $rabbitMqProducer;
|
||||
$this->redisProducer = $redisProducer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/pocket_csv", name="import_pocket_csv")
|
||||
*/
|
||||
public function indexAction(Request $request, TranslatorInterface $translator)
|
||||
{
|
||||
return parent::indexAction($request, $translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getImportService()
|
||||
{
|
||||
if ($this->craueConfig->get('import_with_rabbitmq')) {
|
||||
$this->pocketCsvImport->setProducer($this->rabbitMqProducer);
|
||||
} elseif ($this->craueConfig->get('import_with_redis')) {
|
||||
$this->pocketCsvImport->setProducer($this->redisProducer);
|
||||
}
|
||||
|
||||
return $this->pocketCsvImport;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getImportTemplate()
|
||||
{
|
||||
return '@WallabagImport/PocketCsv/index.html.twig';
|
||||
}
|
||||
}
|
156
src/Wallabag/ImportBundle/Import/PocketCsvImport.php
Normal file
156
src/Wallabag/ImportBundle/Import/PocketCsvImport.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Import;
|
||||
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
|
||||
class PocketCsvImport extends AbstractImport
|
||||
{
|
||||
protected $filepath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Pocket CSV';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return 'import_pocket_csv';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return 'import.pocket_csv.description';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file path to the csv file.
|
||||
*
|
||||
* @param string $filepath
|
||||
*/
|
||||
public function setFilepath($filepath)
|
||||
{
|
||||
$this->filepath = $filepath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateEntry(array $importedEntry)
|
||||
{
|
||||
if (empty($importedEntry['url'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
if (!$this->user) {
|
||||
$this->logger->error('Pocket CSV Import: user is not defined');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
|
||||
$this->logger->error('Pocket CSV Import: unable to read file', ['filepath' => $this->filepath]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$entries = [];
|
||||
$handle = fopen($this->filepath, 'r');
|
||||
while (false !== ($data = fgetcsv($handle, 10240))) {
|
||||
if ('title' === $data[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entries[] = [
|
||||
'url' => $data[1],
|
||||
'title' => $data[0],
|
||||
'is_archived' => 'archive' === $data[4],
|
||||
'created_at' => $data[2],
|
||||
'tags' => $data[3],
|
||||
];
|
||||
}
|
||||
fclose($handle);
|
||||
|
||||
if (empty($entries)) {
|
||||
$this->logger->error('PocketCsvImport: no entries in imported file');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->parseEntries($entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseEntry(array $importedEntry)
|
||||
{
|
||||
$existingEntry = $this->em
|
||||
->getRepository(Entry::class)
|
||||
->findByUrlAndUserId($importedEntry['url'], $this->user->getId());
|
||||
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$entry = new Entry($this->user);
|
||||
$entry->setUrl($importedEntry['url']);
|
||||
$entry->setTitle($importedEntry['title']);
|
||||
|
||||
// update entry with content (in case fetching failed, the given entry will be return)
|
||||
$this->fetchContent($entry, $importedEntry['url'], $importedEntry);
|
||||
|
||||
if (!empty($importedEntry['tags'])) {
|
||||
$tags = str_replace('|', ',', $importedEntry['tags']);
|
||||
$this->tagsAssigner->assignTagsToEntry(
|
||||
$entry,
|
||||
$tags,
|
||||
$this->em->getUnitOfWork()->getScheduledEntityInsertions()
|
||||
);
|
||||
}
|
||||
|
||||
$entry->updateArchived($importedEntry['is_archived']);
|
||||
$entry->setCreatedAt(\DateTime::createFromFormat('U', $importedEntry['created_at']));
|
||||
|
||||
$this->em->persist($entry);
|
||||
++$this->importedEntries;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setEntryAsRead(array $importedEntry)
|
||||
{
|
||||
$importedEntry['is_archived'] = 'archive';
|
||||
|
||||
return $importedEntry;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
{% extends "@WallabagCore/layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'import.pocket_csv.page_title'|trans }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card-panel settings">
|
||||
{% include '@WallabagImport/Import/_information.html.twig' %}
|
||||
|
||||
<div class="row">
|
||||
<blockquote>{{ import.description|trans|raw }}</blockquote>
|
||||
<p>{{ 'import.pocket_csv.how_to'|trans }}</p>
|
||||
|
||||
<div class="col s12">
|
||||
{{ form_start(form, {'method': 'POST'}) }}
|
||||
{{ form_errors(form) }}
|
||||
<div class="row">
|
||||
<div class="file-field input-field col s12">
|
||||
{{ form_errors(form.file) }}
|
||||
<div class="btn">
|
||||
<span>{{ form.file.vars.label|trans }}</span>
|
||||
{{ form_widget(form.file) }}
|
||||
</div>
|
||||
<div class="file-path-wrapper">
|
||||
<input class="file-path validate" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-field col s6 with-checkbox">
|
||||
<h6>{{ 'import.form.mark_as_read_title'|trans }}</h6>
|
||||
{{ form_widget(form.mark_as_read) }}
|
||||
{{ form_label(form.mark_as_read) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_widget(form.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue