mirror of
https://github.com/wallabag/wallabag.git
synced 2024-12-26 09:30:30 +00:00
Add Delicious import
Since 2021, you can export again your data \o/ Also fix indentation in json fixtures files.
This commit is contained in:
parent
890c7d0bfa
commit
dd9d6a4c64
23 changed files with 984 additions and 370 deletions
|
@ -248,6 +248,11 @@ old_sound_rabbit_mq:
|
|||
exchange_options:
|
||||
name: 'wallabag.import.pinboard'
|
||||
type: topic
|
||||
import_delicious:
|
||||
connection: default
|
||||
exchange_options:
|
||||
name: 'wallabag.import.delicious'
|
||||
type: topic
|
||||
import_instapaper:
|
||||
connection: default
|
||||
exchange_options:
|
||||
|
@ -315,6 +320,15 @@ old_sound_rabbit_mq:
|
|||
name: 'wallabag.import.pinboard'
|
||||
callback: wallabag_import.consumer.amqp.pinboard
|
||||
qos_options: {prefetch_count: "%rabbitmq_prefetch_count%"}
|
||||
import_delicious:
|
||||
connection: default
|
||||
exchange_options:
|
||||
name: 'wallabag.import.delicious'
|
||||
type: topic
|
||||
queue_options:
|
||||
name: 'wallabag.import.delicious'
|
||||
callback: wallabag_import.consumer.amqp.delicious
|
||||
qos_options: {prefetch_count: "%rabbitmq_prefetch_count%"}
|
||||
import_wallabag_v1:
|
||||
connection: default
|
||||
exchange_options:
|
||||
|
|
|
@ -517,6 +517,10 @@ import:
|
|||
page_title: Import > Pinboard
|
||||
description: This importer will import all your Pinboard articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").
|
||||
how_to: Please select your Pinboard export and click on the below button to upload and import it.
|
||||
delicious:
|
||||
page_title: Import > del.icio.us
|
||||
description: This importer will import all your Delicious bookmarks. Since 2021, you can export again your data from it using the export page (https://del.icio.us/export). Choose the "JSON" format and download it (like "delicious_export.2021.02.06_21.10.json").
|
||||
how_to: Please select your Delicious export and click on the below button to upload and import it.
|
||||
developer:
|
||||
page_title: API clients management
|
||||
welcome_message: Welcome to the wallabag API
|
||||
|
|
|
@ -19,7 +19,7 @@ class ImportCommand extends ContainerAwareCommand
|
|||
->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, readability, firefox or chrome', 'v1')
|
||||
->addOption('importer', null, InputOption::VALUE_OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, delicious, readability, firefox or chrome', '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')
|
||||
|
@ -77,6 +77,9 @@ class ImportCommand extends ContainerAwareCommand
|
|||
case 'pinboard':
|
||||
$import = $this->getContainer()->get('wallabag_import.pinboard.import');
|
||||
break;
|
||||
case 'delicious':
|
||||
$import = $this->getContainer()->get('wallabag_import.delicious.import');
|
||||
break;
|
||||
default:
|
||||
$import = $this->getContainer()->get('wallabag_import.wallabag_v1.import');
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class RedisWorkerCommand extends ContainerAwareCommand
|
|||
$this
|
||||
->setName('wallabag:import:redis-worker')
|
||||
->setDescription('Launch Redis worker')
|
||||
->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket, readability, pinboard, firefox, chrome or instapaper')
|
||||
->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket, readability, pinboard, delicious, firefox, chrome or instapaper')
|
||||
->addOption('maxIterations', '', InputOption::VALUE_OPTIONAL, 'Number of iterations before stoping', false)
|
||||
;
|
||||
}
|
||||
|
|
77
src/Wallabag/ImportBundle/Controller/DeliciousController.php
Normal file
77
src/Wallabag/ImportBundle/Controller/DeliciousController.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Wallabag\ImportBundle\Form\Type\UploadImportType;
|
||||
|
||||
class DeliciousController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/delicious", name="import_delicious")
|
||||
*/
|
||||
public function indexAction(Request $request)
|
||||
{
|
||||
$form = $this->createForm(UploadImportType::class);
|
||||
$form->handleRequest($request);
|
||||
|
||||
$delicious = $this->get('wallabag_import.delicious.import');
|
||||
$delicious->setUser($this->getUser());
|
||||
|
||||
if ($this->get('craue_config')->get('import_with_rabbitmq')) {
|
||||
$delicious->setProducer($this->get('old_sound_rabbit_mq.import_delicious_producer'));
|
||||
} elseif ($this->get('craue_config')->get('import_with_redis')) {
|
||||
$delicious->setProducer($this->get('wallabag_import.producer.redis.delicious'));
|
||||
}
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$file = $form->get('file')->getData();
|
||||
$markAsRead = $form->get('mark_as_read')->getData();
|
||||
$name = 'delicious_' . $this->getUser()->getId() . '.json';
|
||||
|
||||
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
|
||||
$res = $delicious
|
||||
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
|
||||
->setMarkAsRead($markAsRead)
|
||||
->import();
|
||||
|
||||
$message = 'flashes.import.notice.failed';
|
||||
|
||||
if (true === $res) {
|
||||
$summary = $delicious->getSummary();
|
||||
$message = $this->get('translator')->trans('flashes.import.notice.summary', [
|
||||
'%imported%' => $summary['imported'],
|
||||
'%skipped%' => $summary['skipped'],
|
||||
]);
|
||||
|
||||
if (0 < $summary['queued']) {
|
||||
$message = $this->get('translator')->trans('flashes.import.notice.summary_with_queue', [
|
||||
'%queued%' => $summary['queued'],
|
||||
]);
|
||||
}
|
||||
|
||||
unlink($this->getParameter('wallabag_import.resource_dir') . '/' . $name);
|
||||
}
|
||||
|
||||
$this->get('session')->getFlashBag()->add(
|
||||
'notice',
|
||||
$message
|
||||
);
|
||||
|
||||
return $this->redirect($this->generateUrl('homepage'));
|
||||
}
|
||||
|
||||
$this->get('session')->getFlashBag()->add(
|
||||
'notice',
|
||||
'flashes.import.notice.failed_on_file'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->render('WallabagImportBundle:Delicious:index.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'import' => $delicious,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ class ImportController extends Controller
|
|||
+ $this->getTotalMessageInRabbitQueue('chrome')
|
||||
+ $this->getTotalMessageInRabbitQueue('instapaper')
|
||||
+ $this->getTotalMessageInRabbitQueue('pinboard')
|
||||
+ $this->getTotalMessageInRabbitQueue('delicious')
|
||||
+ $this->getTotalMessageInRabbitQueue('elcurator')
|
||||
;
|
||||
} catch (\Exception $e) {
|
||||
|
@ -60,6 +61,7 @@ class ImportController extends Controller
|
|||
+ $redis->llen('wallabag.import.chrome')
|
||||
+ $redis->llen('wallabag.import.instapaper')
|
||||
+ $redis->llen('wallabag.import.pinboard')
|
||||
+ $redis->llen('wallabag.import.delicious')
|
||||
+ $redis->llen('wallabag.import.elcurator')
|
||||
;
|
||||
} catch (\Exception $e) {
|
||||
|
|
151
src/Wallabag/ImportBundle/Import/DeliciousImport.php
Normal file
151
src/Wallabag/ImportBundle/Import/DeliciousImport.php
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?php
|
||||
|
||||
namespace Wallabag\ImportBundle\Import;
|
||||
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
|
||||
class DeliciousImport extends AbstractImport
|
||||
{
|
||||
private $filepath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Delicious';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return 'import_delicious';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return 'import.delicious.description';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file path to the json file.
|
||||
*
|
||||
* @param string $filepath
|
||||
*/
|
||||
public function setFilepath($filepath)
|
||||
{
|
||||
$this->filepath = $filepath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import()
|
||||
{
|
||||
if (!$this->user) {
|
||||
$this->logger->error('DeliciousImport: user is not defined');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
|
||||
$this->logger->error('DeliciousImport: unable to read file', ['filepath' => $this->filepath]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents($this->filepath), true);
|
||||
|
||||
if (empty($data)) {
|
||||
$this->logger->error('DeliciousImport: no entries in imported file');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->parseEntries($data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateEntry(array $importedEntry)
|
||||
{
|
||||
if (empty($importedEntry['url'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseEntry(array $importedEntry)
|
||||
{
|
||||
$existingEntry = $this->em
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
->findByUrlAndUserId($importedEntry['url'], $this->user->getId());
|
||||
|
||||
if (false !== $existingEntry) {
|
||||
++$this->skippedEntries;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'title' => $importedEntry['title'],
|
||||
'url' => $importedEntry['url'],
|
||||
'is_archived' => $this->markAsRead,
|
||||
'is_starred' => false,
|
||||
'created_at' => $importedEntry['created'],
|
||||
'tags' => $importedEntry['tags'],
|
||||
];
|
||||
|
||||
$entry = new Entry($this->user);
|
||||
$entry->setUrl($data['url']);
|
||||
$entry->setTitle($data['title']);
|
||||
|
||||
// update entry with content (in case fetching failed, the given entry will be return)
|
||||
$this->fetchContent($entry, $data['url'], $data);
|
||||
|
||||
if (!empty($data['tags'])) {
|
||||
$this->tagsAssigner->assignTagsToEntry(
|
||||
$entry,
|
||||
$data['tags'],
|
||||
$this->em->getUnitOfWork()->getScheduledEntityInsertions()
|
||||
);
|
||||
}
|
||||
|
||||
$entry->updateArchived($data['is_archived']);
|
||||
$entry->setStarred($data['is_starred']);
|
||||
$entry->setCreatedAt(\DateTime::createFromFormat('U', $data['created_at']));
|
||||
|
||||
$this->em->persist($entry);
|
||||
++$this->importedEntries;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setEntryAsRead(array $importedEntry)
|
||||
{
|
||||
return $importedEntry;
|
||||
}
|
||||
}
|
|
@ -32,6 +32,14 @@ services:
|
|||
- "@wallabag_import.pinboard.import"
|
||||
- "@event_dispatcher"
|
||||
- "@logger"
|
||||
wallabag_import.consumer.amqp.delicious:
|
||||
class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
|
||||
arguments:
|
||||
- "@doctrine.orm.entity_manager"
|
||||
- "@wallabag_user.user_repository"
|
||||
- "@wallabag_import.delicious.import"
|
||||
- "@event_dispatcher"
|
||||
- "@logger"
|
||||
wallabag_import.consumer.amqp.wallabag_v1:
|
||||
class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
|
||||
arguments:
|
||||
|
|
|
@ -63,6 +63,27 @@ services:
|
|||
- "@event_dispatcher"
|
||||
- "@logger"
|
||||
|
||||
# delicious
|
||||
wallabag_import.queue.redis.delicious:
|
||||
class: Simpleue\Queue\RedisQueue
|
||||
arguments:
|
||||
- "@wallabag_core.redis.client"
|
||||
- "wallabag.import.delicious"
|
||||
|
||||
wallabag_import.producer.redis.delicious:
|
||||
class: Wallabag\ImportBundle\Redis\Producer
|
||||
arguments:
|
||||
- "@wallabag_import.queue.redis.delicious"
|
||||
|
||||
wallabag_import.consumer.redis.delicious:
|
||||
class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
|
||||
arguments:
|
||||
- "@doctrine.orm.entity_manager"
|
||||
- "@wallabag_user.user_repository"
|
||||
- "@wallabag_import.delicious.import"
|
||||
- "@event_dispatcher"
|
||||
- "@logger"
|
||||
|
||||
# pocket
|
||||
wallabag_import.queue.redis.pocket:
|
||||
class: Simpleue\Queue\RedisQueue
|
||||
|
|
|
@ -96,6 +96,18 @@ services:
|
|||
tags:
|
||||
- { name: wallabag_import.import, alias: pinboard }
|
||||
|
||||
wallabag_import.delicious.import:
|
||||
class: Wallabag\ImportBundle\Import\DeliciousImport
|
||||
arguments:
|
||||
- "@doctrine.orm.entity_manager"
|
||||
- "@wallabag_core.content_proxy"
|
||||
- "@wallabag_core.tags_assigner"
|
||||
- "@event_dispatcher"
|
||||
calls:
|
||||
- [ setLogger, [ "@logger" ]]
|
||||
tags:
|
||||
- { name: wallabag_import.import, alias: delicious }
|
||||
|
||||
wallabag_import.firefox.import:
|
||||
class: Wallabag\ImportBundle\Import\FirefoxImport
|
||||
arguments:
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
{% extends "WallabagCoreBundle::layout.html.twig" %}
|
||||
|
||||
{% block title %}{{ 'import.delicious.page_title'|trans }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card-panel settings">
|
||||
{% include 'WallabagImportBundle:Import:_information.html.twig' %}
|
||||
|
||||
<div class="row">
|
||||
<blockquote>{{ import.description|trans }}</blockquote>
|
||||
<p>{{ 'import.delicious.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 %}
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Wallabag\ImportBundle\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
|
||||
|
||||
class DeliciousControllerTest extends WallabagCoreTestCase
|
||||
{
|
||||
public function testImportDelicious()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
$this->assertSame(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
|
||||
$this->assertSame(1, $crawler->filter('input[type=file]')->count());
|
||||
}
|
||||
|
||||
public function testImportDeliciousWithRabbitEnabled()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
|
||||
$client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 1);
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
$this->assertSame(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
|
||||
$this->assertSame(1, $crawler->filter('input[type=file]')->count());
|
||||
|
||||
$client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 0);
|
||||
}
|
||||
|
||||
public function testImportDeliciousBadFile()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
$form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
|
||||
|
||||
$data = [
|
||||
'upload_import_file[file]' => '',
|
||||
];
|
||||
|
||||
$client->submit($form, $data);
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testImportDeliciousWithRedisEnabled()
|
||||
{
|
||||
$this->checkRedis();
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
$client->getContainer()->get('craue_config')->set('import_with_redis', 1);
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
|
||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||
$this->assertSame(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
|
||||
$this->assertSame(1, $crawler->filter('input[type=file]')->count());
|
||||
|
||||
$form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
|
||||
|
||||
$file = new UploadedFile(__DIR__ . '/../fixtures/delicious_export.2021.02.06_21.10.json', 'delicious.json');
|
||||
|
||||
$data = [
|
||||
'upload_import_file[file]' => $file,
|
||||
];
|
||||
|
||||
$client->submit($form, $data);
|
||||
|
||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||
|
||||
$crawler = $client->followRedirect();
|
||||
|
||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||
$this->assertStringContainsString('flashes.import.notice.summary', $body[0]);
|
||||
|
||||
$this->assertNotEmpty($client->getContainer()->get('wallabag_core.redis.client')->lpop('wallabag.import.delicious'));
|
||||
|
||||
$client->getContainer()->get('craue_config')->set('import_with_redis', 0);
|
||||
}
|
||||
|
||||
public function testImportDeliciousWithFile()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
$form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
|
||||
|
||||
$file = new UploadedFile(__DIR__ . '/../fixtures/delicious_export.2021.02.06_21.10.json', 'delicious.json');
|
||||
|
||||
$data = [
|
||||
'upload_import_file[file]' => $file,
|
||||
];
|
||||
|
||||
$client->submit($form, $data);
|
||||
|
||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||
|
||||
$crawler = $client->followRedirect();
|
||||
|
||||
$content = $client->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
->findByUrlAndUserId(
|
||||
'https://feross.org/spoofmac/',
|
||||
$this->getLoggedInUserId()
|
||||
);
|
||||
|
||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||
$this->assertStringContainsString('flashes.import.notice.summary', $body[0]);
|
||||
|
||||
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
|
||||
|
||||
$tags = $content->getTags();
|
||||
$this->assertContains('osx', $tags, 'It includes the "osx" tag');
|
||||
$this->assertGreaterThanOrEqual(4, count($tags));
|
||||
|
||||
$this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
|
||||
$this->assertSame('2013-01-17', $content->getCreatedAt()->format('Y-m-d'));
|
||||
}
|
||||
|
||||
public function testImportDeliciousWithFileAndMarkAllAsRead()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
$form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
|
||||
|
||||
$file = new UploadedFile(__DIR__ . '/../fixtures/delicious_export.2021.02.06_21.10.json', 'delicious-read.json');
|
||||
|
||||
$data = [
|
||||
'upload_import_file[file]' => $file,
|
||||
'upload_import_file[mark_as_read]' => 1,
|
||||
];
|
||||
|
||||
$client->submit($form, $data);
|
||||
|
||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||
|
||||
$crawler = $client->followRedirect();
|
||||
|
||||
$content1 = $client->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
->findByUrlAndUserId(
|
||||
'https://stackoverflow.com/review/',
|
||||
$this->getLoggedInUserId()
|
||||
);
|
||||
|
||||
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content1);
|
||||
|
||||
$content2 = $client->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
->findByUrlAndUserId(
|
||||
'https://addyosmani.com/basket.js/',
|
||||
$this->getLoggedInUserId()
|
||||
);
|
||||
|
||||
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content2);
|
||||
|
||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||
$this->assertStringContainsString('flashes.import.notice.summary', $body[0]);
|
||||
}
|
||||
|
||||
public function testImportDeliciousWithEmptyFile()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
|
||||
$crawler = $client->request('GET', '/import/delicious');
|
||||
$form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
|
||||
|
||||
$file = new UploadedFile(__DIR__ . '/../fixtures/test.txt', 'test.txt');
|
||||
|
||||
$data = [
|
||||
'upload_import_file[file]' => $file,
|
||||
];
|
||||
|
||||
$client->submit($form, $data);
|
||||
|
||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||
|
||||
$crawler = $client->followRedirect();
|
||||
|
||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||
$this->assertStringContainsString('flashes.import.notice.failed', $body[0]);
|
||||
}
|
||||
}
|
|
@ -1,36 +1,38 @@
|
|||
{
|
||||
"checksum": "f3aa0e9c0edad632a246f7e98ec64918",
|
||||
"roots": {
|
||||
"bookmark_bar": {
|
||||
"children": [ {
|
||||
"date_added": "13118850929335823",
|
||||
"id": "6",
|
||||
"name": "\"La multiplication des chefs de projet est une catastrophe managériale majeure\", affirme le sociologue François Dupuy - Ressources humaines",
|
||||
"type": "url",
|
||||
"url": "http://www.usinenouvelle.com/article/la-multiplication-des-chefs-de-projet-est-une-catastrophe-manageriale-majeure-affirme-le-sociologue-francois-dupuy.N307730"
|
||||
} ],
|
||||
"date_added": "13118829474385693",
|
||||
"date_modified": "13118850929335823",
|
||||
"id": "1",
|
||||
"name": "Barre de favoris",
|
||||
"type": "folder"
|
||||
},
|
||||
"other": {
|
||||
"children": [ ],
|
||||
"date_added": "13118829474385701",
|
||||
"date_modified": "0",
|
||||
"id": "2",
|
||||
"name": "Autres favoris",
|
||||
"type": "folder"
|
||||
},
|
||||
"synced": {
|
||||
"children": [ ],
|
||||
"date_added": "13118829474385702",
|
||||
"date_modified": "0",
|
||||
"id": "3",
|
||||
"name": "Favoris sur mobile",
|
||||
"type": "folder"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
"checksum": "f3aa0e9c0edad632a246f7e98ec64918",
|
||||
"roots": {
|
||||
"bookmark_bar": {
|
||||
"children": [
|
||||
{
|
||||
"date_added": "13118850929335823",
|
||||
"id": "6",
|
||||
"name": "\"La multiplication des chefs de projet est une catastrophe managériale majeure\", affirme le sociologue François Dupuy - Ressources humaines",
|
||||
"type": "url",
|
||||
"url": "http://www.usinenouvelle.com/article/la-multiplication-des-chefs-de-projet-est-une-catastrophe-manageriale-majeure-affirme-le-sociologue-francois-dupuy.N307730"
|
||||
}
|
||||
],
|
||||
"date_added": "13118829474385693",
|
||||
"date_modified": "13118850929335823",
|
||||
"id": "1",
|
||||
"name": "Barre de favoris",
|
||||
"type": "folder"
|
||||
},
|
||||
"other": {
|
||||
"children": [],
|
||||
"date_added": "13118829474385701",
|
||||
"date_modified": "0",
|
||||
"id": "2",
|
||||
"name": "Autres favoris",
|
||||
"type": "folder"
|
||||
},
|
||||
"synced": {
|
||||
"children": [],
|
||||
"date_added": "13118829474385702",
|
||||
"date_modified": "0",
|
||||
"id": "3",
|
||||
"name": "Favoris sur mobile",
|
||||
"type": "folder"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
[
|
||||
{
|
||||
"title": "basket.js - a simple script loader that caches scripts with localStorage",
|
||||
"tags": [
|
||||
"basket",
|
||||
"javascript",
|
||||
"loader",
|
||||
"localStorage"
|
||||
],
|
||||
"url": "https://addyosmani.com/basket.js/",
|
||||
"description": "\"A simple (proof-of-concept) script loader that caches scripts with localStorage\"",
|
||||
"created": "1358531607",
|
||||
"others": 9,
|
||||
"owner": "maciej",
|
||||
"private": "0"
|
||||
},
|
||||
{
|
||||
"title": "Review - Stack Overflow",
|
||||
"tags": [
|
||||
""
|
||||
],
|
||||
"url": "https://stackoverflow.com/review/",
|
||||
"description": "",
|
||||
"created": "1358457437",
|
||||
"others": 84,
|
||||
"owner": "maciej",
|
||||
"private": "0"
|
||||
},
|
||||
{
|
||||
"title": "Announcing SpoofMAC - Spoof your MAC address in Mac OS X",
|
||||
"tags": [
|
||||
"MAC_address",
|
||||
"osx",
|
||||
"mac",
|
||||
"spoof"
|
||||
],
|
||||
"url": "https://feross.org/spoofmac/",
|
||||
"description": "",
|
||||
"created": "1358425796",
|
||||
"others": 6,
|
||||
"owner": "maciej",
|
||||
"private": "0"
|
||||
}
|
||||
]
|
|
@ -1,13 +1,13 @@
|
|||
[
|
||||
{
|
||||
"created_at": "2015-09-09 11:10:32 UTC",
|
||||
"title": "Qualité de code - Intégration de php-git-hooks dans Symfony2 - Experts Symfony et Drupal - Lexik",
|
||||
"url": "https://devblog.lexik.fr/git/qualite-de-code-integration-de-php-git-hooks-dans-symfony2-2842",
|
||||
"description": null,
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
],
|
||||
"is_saved": true
|
||||
}
|
||||
{
|
||||
"created_at": "2015-09-09 11:10:32 UTC",
|
||||
"title": "Qualité de code - Intégration de php-git-hooks dans Symfony2 - Experts Symfony et Drupal - Lexik",
|
||||
"url": "https://devblog.lexik.fr/git/qualite-de-code-integration-de-php-git-hooks-dans-symfony2-2842",
|
||||
"description": null,
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
],
|
||||
"is_saved": true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,63 +1,63 @@
|
|||
{
|
||||
"guid": "root________",
|
||||
"title": "",
|
||||
"index": 0,
|
||||
"dateAdded": 1388166091504000,
|
||||
"lastModified": 1472897622350000,
|
||||
"id": 1,
|
||||
"type": "text/x-moz-place-container",
|
||||
"root": "placesRoot",
|
||||
"children": [
|
||||
"guid": "root________",
|
||||
"title": "",
|
||||
"index": 0,
|
||||
"dateAdded": 1388166091504000,
|
||||
"lastModified": 1472897622350000,
|
||||
"id": 1,
|
||||
"type": "text/x-moz-place-container",
|
||||
"root": "placesRoot",
|
||||
"children": [
|
||||
{
|
||||
"guid": "toolbar_____",
|
||||
"title": "Barre personnelle",
|
||||
"index": 1,
|
||||
"dateAdded": 1388166091504000,
|
||||
"lastModified": 1472897622263000,
|
||||
"id": 3,
|
||||
"annos": [
|
||||
{
|
||||
"guid": "toolbar_____",
|
||||
"title": "Barre personnelle",
|
||||
"index": 1,
|
||||
"dateAdded": 1388166091504000,
|
||||
"lastModified": 1472897622263000,
|
||||
"id": 3,
|
||||
"annos": [
|
||||
{
|
||||
"name": "bookmarkProperties/description",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": "Ajoutez des marque-pages dans ce dossier pour les voir apparaître sur votre barre personnelle"
|
||||
}
|
||||
],
|
||||
"type": "text/x-moz-place-container",
|
||||
"root": "toolbarFolder",
|
||||
"children": [
|
||||
{
|
||||
"guid": "tard77lzbC5H",
|
||||
"title": "Orange offre un meilleur réseau mobile que Bouygues et SFR, Free derrière - L'Express L'Expansion",
|
||||
"index": 1,
|
||||
"dateAdded": 1388166091644000,
|
||||
"lastModified": 1388166091644000,
|
||||
"tags":"test,tag",
|
||||
"id": 4,
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "http://lexpansion.lexpress.fr/high-tech/orange-offre-un-meilleur-reseau-mobile-que-bouygues-et-sfr-free-derriere_1811554.html"
|
||||
},
|
||||
{
|
||||
"guid": "E385l9vZ_LVn",
|
||||
"title": "Le journaliste et cinéaste Claude Lanzmann est mort",
|
||||
"index": 1,
|
||||
"dateAdded": 1388166091544000,
|
||||
"lastModified": 1388166091545000,
|
||||
"id": 5,
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "https://www.lemonde.fr/disparitions/article/2018/07/05/le-journaliste-et-cineaste-claude-lanzmann-est-mort_5326313_3382.html"
|
||||
}
|
||||
]
|
||||
"name": "bookmarkProperties/description",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": "Ajoutez des marque-pages dans ce dossier pour les voir apparaître sur votre barre personnelle"
|
||||
}
|
||||
],
|
||||
"type": "text/x-moz-place-container",
|
||||
"root": "toolbarFolder",
|
||||
"children": [
|
||||
{
|
||||
"guid": "tard77lzbC5H",
|
||||
"title": "Orange offre un meilleur réseau mobile que Bouygues et SFR, Free derrière - L'Express L'Expansion",
|
||||
"index": 1,
|
||||
"dateAdded": 1388166091644000,
|
||||
"lastModified": 1388166091644000,
|
||||
"tags": "test,tag",
|
||||
"id": 4,
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "http://lexpansion.lexpress.fr/high-tech/orange-offre-un-meilleur-reseau-mobile-que-bouygues-et-sfr-free-derriere_1811554.html"
|
||||
},
|
||||
{
|
||||
"guid": "unfiled_____",
|
||||
"title": "Autres marque-pages",
|
||||
"index": 3,
|
||||
"dateAdded": 1388166091504000,
|
||||
"lastModified": 1388166091542000,
|
||||
"id": 6,
|
||||
"type": "text/x-moz-place-container",
|
||||
"root": "unfiledBookmarksFolder"
|
||||
"guid": "E385l9vZ_LVn",
|
||||
"title": "Le journaliste et cinéaste Claude Lanzmann est mort",
|
||||
"index": 1,
|
||||
"dateAdded": 1388166091544000,
|
||||
"lastModified": 1388166091545000,
|
||||
"id": 5,
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "https://www.lemonde.fr/disparitions/article/2018/07/05/le-journaliste-et-cineaste-claude-lanzmann-est-mort_5326313_3382.html"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"guid": "unfiled_____",
|
||||
"title": "Autres marque-pages",
|
||||
"index": 3,
|
||||
"dateAdded": 1388166091504000,
|
||||
"lastModified": 1388166091542000,
|
||||
"id": 6,
|
||||
"type": "text/x-moz-place-container",
|
||||
"root": "unfiledBookmarksFolder"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,35 @@
|
|||
[{"href":"https:\/\/developers.google.com\/web\/updates\/2016\/07\/infinite-scroller","description":"Complexities of an Infinite Scroller","extended":"TL;DR: Re-use your DOM elements and remove the ones that are far away from the viewport. Use placeholders to account for delayed data","meta":"21ff61c6f648901168f9e6119f53df7d","hash":"e69b65724cca1c585b446d4c47865d76","time":"2016-10-31T15:57:56Z","shared":"yes","toread":"no","tags":"infinite dom performance scroll"},
|
||||
{"href":"https:\/\/ma.ttias.be\/varnish-explained\/","description":"Varnish (explained) for PHP developers","extended":"A few months ago, I gave a presentation at LaraconEU in Amsterdam titled \"Varnish for PHP developers\". The generic title of that presentation is actually Varnish Explained and this is a write-up of that presentation, the video and the slides.","meta":"d32ad9fac2ed29da4aec12c562e9afb1","hash":"21dd6bdda8ad62666a2c9e79f6e80f98","time":"2016-10-26T06:43:03Z","shared":"yes","toread":"no","tags":"varnish PHP"},
|
||||
{"href":"https:\/\/ilia.ws\/files\/nginx_torontophpug.pdf","description":"Nginx Tricks for PHP Developers","extended":"","meta":"9adbb5c4ca6760e335b920800d88c70a","hash":"0189bb08f8bd0122c6544bed4624c546","time":"2016-10-05T07:11:27Z","shared":"yes","toread":"no","tags":"nginx PHP best_practice"}]
|
||||
[
|
||||
{
|
||||
"href": "https://developers.google.com/web/updates/2016/07/infinite-scroller",
|
||||
"description": "Complexities of an Infinite Scroller",
|
||||
"extended": "TL;DR: Re-use your DOM elements and remove the ones that are far away from the viewport. Use placeholders to account for delayed data",
|
||||
"meta": "21ff61c6f648901168f9e6119f53df7d",
|
||||
"hash": "e69b65724cca1c585b446d4c47865d76",
|
||||
"time": "2016-10-31T15:57:56Z",
|
||||
"shared": "yes",
|
||||
"toread": "no",
|
||||
"tags": "infinite dom performance scroll"
|
||||
},
|
||||
{
|
||||
"href": "https://ma.ttias.be/varnish-explained/",
|
||||
"description": "Varnish (explained) for PHP developers",
|
||||
"extended": "A few months ago, I gave a presentation at LaraconEU in Amsterdam titled \"Varnish for PHP developers\". The generic title of that presentation is actually Varnish Explained and this is a write-up of that presentation, the video and the slides.",
|
||||
"meta": "d32ad9fac2ed29da4aec12c562e9afb1",
|
||||
"hash": "21dd6bdda8ad62666a2c9e79f6e80f98",
|
||||
"time": "2016-10-26T06:43:03Z",
|
||||
"shared": "yes",
|
||||
"toread": "no",
|
||||
"tags": "varnish PHP"
|
||||
},
|
||||
{
|
||||
"href": "https://ilia.ws/files/nginx_torontophpug.pdf",
|
||||
"description": "Nginx Tricks for PHP Developers",
|
||||
"extended": "",
|
||||
"meta": "9adbb5c4ca6760e335b920800d88c70a",
|
||||
"hash": "0189bb08f8bd0122c6544bed4624c546",
|
||||
"time": "2016-10-05T07:11:27Z",
|
||||
"shared": "yes",
|
||||
"toread": "no",
|
||||
"tags": "nginx PHP best_practice"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
{
|
||||
"bookmarks": [
|
||||
{
|
||||
"article__excerpt": "This is a guest post from Moritz Beller from the Delft University of Technology in The Netherlands. His team produced amazing research on several million Travis CI builds, creating invaluable…",
|
||||
"favorite": false,
|
||||
"date_archived": "2016-08-02T06:49:30",
|
||||
"article__url": "https://blog.travis-ci.com/2016-07-28-what-we-learned-from-analyzing-2-million-travis-builds/",
|
||||
"date_added": "2016-08-01T05:24:16",
|
||||
"date_favorited": null,
|
||||
"article__title": "Travis",
|
||||
"archive": true
|
||||
},
|
||||
{
|
||||
"article__excerpt": "The GraphQL Type system describes the capabilities of a GraphQL server and is used to determine if a query is valid. The type system also describes the input types of query variables to determine if…",
|
||||
"favorite": false,
|
||||
"date_archived": "2016-07-19T06:48:31",
|
||||
"article__url": "https://facebook.github.io/graphql/October2016/",
|
||||
"date_added": "2016-06-24T17:50:16",
|
||||
"date_favorited": null,
|
||||
"article__title": "GraphQL",
|
||||
"archive": true
|
||||
}
|
||||
],
|
||||
"recommendations": []
|
||||
"bookmarks": [
|
||||
{
|
||||
"article__excerpt": "This is a guest post from Moritz Beller from the Delft University of Technology in The Netherlands. His team produced amazing research on several million Travis CI builds, creating invaluable…",
|
||||
"favorite": false,
|
||||
"date_archived": "2016-08-02T06:49:30",
|
||||
"article__url": "https://blog.travis-ci.com/2016-07-28-what-we-learned-from-analyzing-2-million-travis-builds/",
|
||||
"date_added": "2016-08-01T05:24:16",
|
||||
"date_favorited": null,
|
||||
"article__title": "Travis",
|
||||
"archive": true
|
||||
},
|
||||
{
|
||||
"article__excerpt": "The GraphQL Type system describes the capabilities of a GraphQL server and is used to determine if a query is valid. The type system also describes the input types of query variables to determine if…",
|
||||
"favorite": false,
|
||||
"date_archived": "2016-07-19T06:48:31",
|
||||
"article__url": "https://facebook.github.io/graphql/October2016/",
|
||||
"date_added": "2016-06-24T17:50:16",
|
||||
"date_favorited": null,
|
||||
"article__title": "GraphQL",
|
||||
"archive": true
|
||||
}
|
||||
],
|
||||
"recommendations": []
|
||||
}
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
{
|
||||
"bookmarks": [
|
||||
{
|
||||
"article__excerpt": "When Twitter started it had so much promise to change the way we communicate. But now it has been ruined by the amount of garbage and hate we have to wade through. It’s like that polluted…",
|
||||
"favorite": false,
|
||||
"date_archived": null,
|
||||
"article__url": "https://venngage.com/blog/hashtags-are-worthless/",
|
||||
"date_added": "2016-08-25T12:05:00",
|
||||
"date_favorited": null,
|
||||
"article__title": "We Looked At 167,943 Tweets & Found Out Hashtags Are Worthless",
|
||||
"archive": false
|
||||
},
|
||||
{
|
||||
"article__title": "No title found",
|
||||
"article__url": "http://news.nationalgeographic.com/2016/02/160211-albatrosses-mothers-babies-animals-science/&sf20739758=1",
|
||||
"archive": false,
|
||||
"date_added": "2016-09-08T11:55:58+0200",
|
||||
"favorite": true
|
||||
},
|
||||
{
|
||||
"archive": 0,
|
||||
"date_added": "2016-09-08T11:55:58+0200",
|
||||
"favorite": 0,
|
||||
"article__title": "Bordeaux: Poche, chocolatine… Une association traduit aux étudiants étrangers les mots du Sud-Ouest",
|
||||
"article__url": "https://www.20minutes.fr/bordeaux/2120479-20170823-bordeaux-poche-chocolatine-association-traduit-etudiants-etrangers-mots-sud-ouest"
|
||||
}
|
||||
],
|
||||
"recommendations": []
|
||||
"bookmarks": [
|
||||
{
|
||||
"article__excerpt": "When Twitter started it had so much promise to change the way we communicate. But now it has been ruined by the amount of garbage and hate we have to wade through. It’s like that polluted…",
|
||||
"favorite": false,
|
||||
"date_archived": null,
|
||||
"article__url": "https://venngage.com/blog/hashtags-are-worthless/",
|
||||
"date_added": "2016-08-25T12:05:00",
|
||||
"date_favorited": null,
|
||||
"article__title": "We Looked At 167,943 Tweets & Found Out Hashtags Are Worthless",
|
||||
"archive": false
|
||||
},
|
||||
{
|
||||
"article__title": "No title found",
|
||||
"article__url": "http://news.nationalgeographic.com/2016/02/160211-albatrosses-mothers-babies-animals-science/&sf20739758=1",
|
||||
"archive": false,
|
||||
"date_added": "2016-09-08T11:55:58+0200",
|
||||
"favorite": true
|
||||
},
|
||||
{
|
||||
"archive": 0,
|
||||
"date_added": "2016-09-08T11:55:58+0200",
|
||||
"favorite": 0,
|
||||
"article__title": "Bordeaux: Poche, chocolatine… Une association traduit aux étudiants étrangers les mots du Sud-Ouest",
|
||||
"article__url": "https://www.20minutes.fr/bordeaux/2120479-20170823-bordeaux-poche-chocolatine-association-traduit-etudiants-etrangers-mots-sud-ouest"
|
||||
}
|
||||
],
|
||||
"recommendations": []
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue