wallabag_import:
resource: "@WallabagImportBundle/Controller/"
type: annotation
- prefix: /
+ prefix: /import
wallabag_api:
resource: "@WallabagApiBundle/Resources/config/routing.yml"
/* force height on non-input field in the settings page */
div.settings div.input-field div, div.settings div.input-field ul {
margin-top: 40px;
-}
\ No newline at end of file
+}
+/* but avoid to kill all file input */
+div.settings div.file-field div {
+ margin-top: inherit;
+}
class ImportController extends Controller
{
/**
- * @Route("/import", name="import")
+ * @Route("/", name="import")
*/
public function importAction()
{
- return $this->render('WallabagImportBundle:Import:index.html.twig', []);
+ return $this->render('WallabagImportBundle:Import:index.html.twig', [
+ 'imports' => $this->get('wallabag_import.chain')->getAll(),
+ ]);
}
}
class PocketController extends Controller
{
/**
- * @Route("/import/pocket", name="import_pocket")
+ * @Route("/pocket", name="import_pocket")
*/
public function indexAction()
{
- return $this->render('WallabagImportBundle:Pocket:index.html.twig', []);
+ return $this->render('WallabagImportBundle:Pocket:index.html.twig', [
+ 'import' => $this->get('wallabag_import.pocket.import'),
+ ]);
}
/**
- * @Route("/import/pocket/auth", name="import_pocket_auth")
+ * @Route("/pocket/auth", name="import_pocket_auth")
*/
public function authAction()
{
}
/**
- * @Route("/import/pocket/callback", name="import_pocket_callback")
+ * @Route("/pocket/callback", name="import_pocket_callback")
*/
public function callbackAction()
{
class WallabagV1Controller extends Controller
{
/**
- * @Route("/import/wallabag-v1", name="import_wallabag_v1")
+ * @Route("/wallabag-v1", name="import_wallabag_v1")
*/
public function indexAction(Request $request)
{
- $importForm = $this->createForm(new UploadImportType());
- $importForm->handleRequest($request);
- $user = $this->getUser();
+ $form = $this->createForm(new UploadImportType());
+ $form->handleRequest($request);
- if ($importForm->isValid()) {
- $file = $importForm->get('file')->getData();
- $name = $user->getId().'.json';
+ $wallabag = $this->get('wallabag_import.wallabag_v1.import');
+
+ if ($form->isValid()) {
+ $file = $form->get('file')->getData();
+ $name = $this->getUser()->getId().'.json';
if (in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
- $wallabag = $this->get('wallabag_import.wallabag_v1.import');
$res = $wallabag
->setUser($this->getUser())
->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
$summary = $wallabag->getSummary();
$message = 'Import summary: '.$summary['imported'].' imported, '.$summary['skipped'].' already saved.';
- @unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
+ unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
}
$this->get('session')->getFlashBag()->add(
}
return $this->render('WallabagImportBundle:WallabagV1:index.html.twig', [
- 'form' => $importForm->createView(),
+ 'form' => $form->createView(),
+ 'import' => $wallabag,
]);
}
}
;
}
- public function getDefaultOptions(array $options)
- {
- return array(
- 'csrf_protection' => false,
- );
- }
-
public function getName()
{
return 'upload_import_file';
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Import;
+
+class ImportChain
+{
+ private $imports;
+
+ public function __construct()
+ {
+ $this->imports = [];
+ }
+
+ /**
+ * Add an import to the chain.
+ *
+ * @param ImportInterface $import
+ * @param string $alias
+ */
+ public function addImport(ImportInterface $import, $alias)
+ {
+ $this->imports[$alias] = $import;
+ }
+
+ /**
+ * Get all imports.
+ *
+ * @return array<ImportInterface>
+ */
+ public function getAll()
+ {
+ return $this->imports;
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Import;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+class ImportCompilerPass implements CompilerPassInterface
+{
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition('wallabag_import.chain')) {
+ return;
+ }
+
+ $definition = $container->getDefinition(
+ 'wallabag_import.chain'
+ );
+
+ $taggedServices = $container->findTaggedServiceIds(
+ 'wallabag_import.import'
+ );
+ foreach ($taggedServices as $id => $tagAttributes) {
+ foreach ($tagAttributes as $attributes) {
+ $definition->addMethodCall(
+ 'addImport',
+ [new Reference($id), $attributes['alias']]
+ );
+ }
+ }
+ }
+}
*/
public function getName();
+ /**
+ * Url to start the import.
+ *
+ * @return string
+ */
+ public function getUrl();
+
/**
* Description of the import.
*
return 'Pocket';
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getUrl()
+ {
+ return 'import_pocket';
+ }
+
/**
* {@inheritdoc}
*/
public function getDescription()
{
- return 'This importer will import all your <a href="https://getpocket.com">Pocket</a> data.';
+ return 'This importer will import all your <a href="https://getpocket.com">Pocket</a> data. Pocket doesn\'t allow us to retrieve content from their service, so the readable content of each article will be re-fetched by Wallabag.';
}
/**
*/
private function parseEntries($entries)
{
+ $i = 1;
+
foreach ($entries as $pocketEntry) {
$url = isset($pocketEntry['resolved_url']) && $pocketEntry['resolved_url'] != '' ? $pocketEntry['resolved_url'] : $pocketEntry['given_url'];
$this->em->persist($entry);
++$this->importedEntries;
+
+ // flush every 20 entries
+ if (($i % 20) === 0) {
+ $em->flush();
+ }
+ ++$i;
}
$this->em->flush();
return 'Wallabag v1';
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getUrl()
+ {
+ return 'import_wallabag_v1';
+ }
+
/**
* {@inheritdoc}
*/
public function getDescription()
{
- return 'This importer will import all your wallabag v1 articles.';
+ return 'This importer will import all your wallabag v1 articles. On your config page, click on "JSON export" in the "Export your wallabag data" section. You will have a "wallabag-export-1-xxxx-xx-xx.json" file.';
}
/**
return false;
}
- $this->parseEntries(json_decode(file_get_contents($this->filepath), true));
+ $data = json_decode(file_get_contents($this->filepath), true);
+
+ if (empty($data)) {
+ return false;
+ }
+
+ $this->parseEntries($data);
return true;
}
*/
private function parseEntries($entries)
{
+ $i = 1;
+
foreach ($entries as $importedEntry) {
$existingEntry = $this->em
->getRepository('WallabagCoreBundle:Entry')
$this->em->persist($entry);
++$this->importedEntries;
+
+ // flush every 20 entries
+ if (($i % 20) === 0) {
+ $em->flush();
+ }
+ ++$i;
}
$this->em->flush();
services:
+ wallabag_import.chain:
+ class: Wallabag\ImportBundle\Import\ImportChain
+
wallabag_import.pocket.client:
class: GuzzleHttp\Client
arguments:
calls:
- [ setClient, [ "@wallabag_import.pocket.client" ] ]
- [ setLogger, [ "@logger" ]]
+ tags:
+ - { name: wallabag_import.import, alias: pocket }
wallabag_import.wallabag_v1.import:
class: Wallabag\ImportBundle\Import\WallabagV1Import
- "@doctrine.orm.entity_manager"
calls:
- [ setLogger, [ "@logger" ]]
+ tags:
+ - { name: wallabag_import.import, alias: wallabag_v1 }
{% extends "WallabagCoreBundle::layout.html.twig" %}
-{% block title %}{% trans %}import{% endtrans %}{% endblock %}
+{% block title %}{% trans %}Import{% endtrans %}{% endblock %}
{% block content %}
-
<div class="row">
<div class="col s12">
<div class="card-panel settings">
{% trans %}Welcome on wallabag importer. Please select your previous service that you want to migrate.{% endtrans %}
<ul>
- <li><a href="{{ path('import_pocket') }}">Pocket</a></li>
- <li><a href="{{ path('import_wallabag_v1') }}">Wallabag v1</a></li>
+ {% for import in imports %}
+ <li>
+ <h5>{{ import.name }}</h5>
+ <blockquote>{{ import.description|raw }}</blockquote>
+ <p><a class="waves-effect waves-light btn" href="{{ path(import.url) }}">Import contents</a></p>
+ </li>
+ {% endfor %}
</ul>
</div>
</div>
{% extends "WallabagCoreBundle::layout.html.twig" %}
-{% block title %}{% trans %}import{% endtrans %}{% endblock %}
+{% block title %}{% trans %}Import > Pocket{% endtrans %}{% endblock %}
{% block content %}
-
<div class="row">
<div class="col s12">
<div class="card-panel settings">
- {% trans %}You can import your data from your Pocket account. You just have to click on the below button and authorize the application to connect to getpocket.com.{% endtrans %}
+ <blockquote>{{ import.description|raw }}</blockquote>
+ <p>{% trans %}You can import your data from your Pocket account. You just have to click on the below button and authorize the application to connect to getpocket.com.{% endtrans %}</p>
<form method="post" action="{{ path('import_pocket_auth') }}">
- <input type="submit" value="Connect to Pocket and import data" />
+ <button class="btn waves-effect waves-light" type="submit" name="action">
+ Connect to Pocket and import data
+ </button>
</form>
</div>
</div>
{% extends "WallabagCoreBundle::layout.html.twig" %}
-{% block title %}{% trans %}import{% endtrans %}{% endblock %}
+{% block title %}{% trans %}Import > Wallabag v1{% endtrans %}{% endblock %}
{% block content %}
-
<div class="row">
<div class="col s12">
<div class="card-panel settings">
<div class="row">
+ <blockquote>{{ import.description|raw }}</blockquote>
+ <p>{% trans %}Please select your wallabag export and click on the below button to upload and import it.{% endtrans %}</p>
<div class="col s12">
{{ form_start(form, {'method': 'POST'}) }}
{{ form_errors(form) }}
<div class="row">
- <div class="input-field col s12">
- <p>{% trans %}Please select your wallabag export and click on the below button to upload and import it.{% endtrans %}</p>
+ <div class="file-field input-field col s12">
{{ form_errors(form.file) }}
- {{ form_widget(form.file) }}
+ <div class="btn">
+ <span>File</span>
+ {{ form_widget(form.file) }}
+ </div>
+ <div class="file-path-wrapper">
+ <input class="file-path validate" type="text">
+ </div>
</div>
</div>
<div class="hidden">{{ form_rest(form) }}</div>
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Tests\Controller;
+
+use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
+
+class ImportControllerTest extends WallabagCoreTestCase
+{
+ public function testLogin()
+ {
+ $client = $this->getClient();
+
+ $client->request('GET', '/import/');
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+ $this->assertContains('login', $client->getResponse()->headers->get('location'));
+ }
+
+ public function testImportList()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(2, $crawler->filter('blockquote')->count());
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Tests\Controller;
+
+use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
+
+class PocketControllerTest extends WallabagCoreTestCase
+{
+ public function testImportPocket()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/pocket');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(1, $crawler->filter('button[type=submit]')->count());
+ }
+
+ public function testImportPocketAuth()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/pocket/auth');
+
+ $this->assertEquals(301, $client->getResponse()->getStatusCode());
+ $this->assertContains('getpocket.com/auth/authorize', $client->getResponse()->headers->get('location'));
+ }
+
+ public function testImportPocketCallbackWithBadToken()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/pocket/callback');
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+ $this->assertContains('import/pocket', $client->getResponse()->headers->get('location'));
+ $this->assertEquals('Import failed, please try again.', $client->getContainer()->get('session')->getFlashBag()->peek('notice')[0]);
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Tests\Controller;
+
+use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+class WallabagV1ControllerTest extends WallabagCoreTestCase
+{
+ public function testImportWallabag()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/wallabag-v1');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
+ $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
+ }
+
+ public function testImportWallabagWithFile()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/wallabag-v1');
+ $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
+
+ $file = new UploadedFile(__DIR__.'/../fixtures/wallabag-v1.json', 'wallabag-v1.json');
+
+ $data = array(
+ 'upload_import_file[file]' => $file,
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text')));
+ $this->assertContains('Import summary', $alert[0]);
+ }
+
+ public function testImportWallabagWithEmptyFile()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/import/wallabag-v1');
+ $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
+
+ $file = new UploadedFile(__DIR__.'/../fixtures/test.txt', 'test.txt');
+
+ $data = array(
+ 'upload_import_file[file]' => $file,
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.messages.success')->extract(array('_text')));
+ $this->assertContains('Import failed, please try again', $alert[0]);
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Tests\Import;
+
+use Wallabag\ImportBundle\Import\ImportChain;
+
+class ImportChainTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetAll()
+ {
+ $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\ImportInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $importChain = new ImportChain();
+ $importChain->addImport($import, 'alias');
+
+ $this->assertCount(1, $importChain->getAll());
+ $this->assertEquals($import, $importChain->getAll()['alias']);
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ImportBundle\Tests\Import;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Wallabag\ImportBundle\Import\ImportCompilerPass;
+
+class ImportCompilerPassTest extends \PHPUnit_Framework_TestCase
+{
+ public function testProcessNoDefinition()
+ {
+ $container = new ContainerBuilder();
+ $res = $this->process($container);
+
+ $this->assertNull($res);
+ }
+
+ public function testProcess()
+ {
+ $container = new ContainerBuilder();
+ $container
+ ->register('wallabag_import.chain')
+ ->setPublic(false)
+ ;
+
+ $container
+ ->register('foo')
+ ->addTag('wallabag_import.import', array('alias' => 'pocket'))
+ ;
+
+ $this->process($container);
+
+ $this->assertTrue($container->hasDefinition('wallabag_import.chain'));
+
+ $definition = $container->getDefinition('wallabag_import.chain');
+ $this->assertTrue($definition->hasMethodCall('addImport'));
+
+ $calls = $definition->getMethodCalls();
+ $this->assertEquals('pocket', $calls[0][1][1]);
+ }
+
+ protected function process(ContainerBuilder $container)
+ {
+ $repeatedPass = new ImportCompilerPass();
+ $repeatedPass->process($container);
+ }
+}
$pocketImport = $this->getPocketImport();
$this->assertEquals('Pocket', $pocketImport->getName());
- $this->assertEquals('This importer will import all your <a href="https://getpocket.com">Pocket</a> data.', $pocketImport->getDescription());
+ $this->assertNotEmpty($pocketImport->getUrl());
+ $this->assertContains('This importer will import all your <a href="https://getpocket.com">Pocket</a> data.', $pocketImport->getDescription());
}
public function testOAuthRequest()
$wallabagV1Import = $this->getWallabagV1Import();
$this->assertEquals('Wallabag v1', $wallabagV1Import->getName());
- $this->assertEquals('This importer will import all your wallabag v1 articles.', $wallabagV1Import->getDescription());
+ $this->assertNotEmpty($wallabagV1Import->getUrl());
+ $this->assertContains('This importer will import all your wallabag v1 articles.', $wallabagV1Import->getDescription());
}
public function testImport()
namespace Wallabag\ImportBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Wallabag\ImportBundle\Import\ImportCompilerPass;
class WallabagImportBundle extends Bundle
{
+ public function build(ContainerBuilder $container)
+ {
+ parent::build($container);
+
+ $container->addCompilerPass(new ImportCompilerPass());
+ }
}