Merge pull request #1670 from wallabag/v2-mark-imported-articles-as-read

Mark all imported articles as read
This commit is contained in:
Nicolas Lœuillet 2016-03-04 16:12:45 +01:00
commit d89908aed3
16 changed files with 359 additions and 8 deletions

View File

@ -215,6 +215,8 @@ Import contents: "Importer les contenus"
Import: "Importer" Import: "Importer"
Import > Wallabag v1: "Importer > Wallabag v1" Import > Wallabag v1: "Importer > Wallabag v1"
Import > Wallabag v2: "Importer > Wallabag v2" Import > Wallabag v2: "Importer > Wallabag v2"
Mark all as read ?: "Marquer tout comme lu ?"
Mark all imported entries as read: "Marquer tous les contenus importés comme lus"
# Quickstart # Quickstart
Quickstart: Pour bien débuter Quickstart: Pour bien débuter

View File

@ -5,6 +5,8 @@ namespace Wallabag\ImportBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
class PocketController extends Controller class PocketController extends Controller
{ {
@ -13,21 +15,31 @@ class PocketController extends Controller
*/ */
public function indexAction() public function indexAction()
{ {
$pocket = $this->get('wallabag_import.pocket.import');
$form = $this->createFormBuilder($pocket)
->add('read', CheckboxType::class, array(
'label' => 'Mark all as read',
'required' => false,
))
->getForm();
return $this->render('WallabagImportBundle:Pocket:index.html.twig', [ return $this->render('WallabagImportBundle:Pocket:index.html.twig', [
'import' => $this->get('wallabag_import.pocket.import'), 'import' => $this->get('wallabag_import.pocket.import'),
'has_consumer_key' => '' == trim($this->get('craue_config')->get('pocket_consumer_key')) ? false : true, 'has_consumer_key' => '' == trim($this->get('craue_config')->get('pocket_consumer_key')) ? false : true,
'form' => $form->createView(),
]); ]);
} }
/** /**
* @Route("/pocket/auth", name="import_pocket_auth") * @Route("/pocket/auth", name="import_pocket_auth")
*/ */
public function authAction() public function authAction(Request $request)
{ {
$requestToken = $this->get('wallabag_import.pocket.import') $requestToken = $this->get('wallabag_import.pocket.import')
->getRequestToken($this->generateUrl('import', array(), UrlGeneratorInterface::ABSOLUTE_URL)); ->getRequestToken($this->generateUrl('import', array(), UrlGeneratorInterface::ABSOLUTE_URL));
$this->get('session')->set('import.pocket.code', $requestToken); $this->get('session')->set('import.pocket.code', $requestToken);
$this->get('session')->set('read', $request->request->get('form')['read']);
return $this->redirect( return $this->redirect(
'https://getpocket.com/auth/authorize?request_token='.$requestToken.'&redirect_uri='.$this->generateUrl('import_pocket_callback', array(), UrlGeneratorInterface::ABSOLUTE_URL), 'https://getpocket.com/auth/authorize?request_token='.$requestToken.'&redirect_uri='.$this->generateUrl('import_pocket_callback', array(), UrlGeneratorInterface::ABSOLUTE_URL),
@ -42,6 +54,8 @@ class PocketController extends Controller
{ {
$message = 'Import failed, please try again.'; $message = 'Import failed, please try again.';
$pocket = $this->get('wallabag_import.pocket.import'); $pocket = $this->get('wallabag_import.pocket.import');
$markAsRead = $this->get('session')->get('read');
$this->get('session')->remove('read');
// something bad happend on pocket side // something bad happend on pocket side
if (false === $pocket->authorize($this->get('session')->get('import.pocket.code'))) { if (false === $pocket->authorize($this->get('session')->get('import.pocket.code'))) {
@ -53,7 +67,7 @@ class PocketController extends Controller
return $this->redirect($this->generateUrl('import_pocket')); return $this->redirect($this->generateUrl('import_pocket'));
} }
if (true === $pocket->import()) { if (true === $pocket->setMarkAsRead($markAsRead)->import()) {
$summary = $pocket->getSummary(); $summary = $pocket->getSummary();
$message = 'Import summary: '.$summary['imported'].' imported, '.$summary['skipped'].' already saved.'; $message = 'Import summary: '.$summary['imported'].' imported, '.$summary['skipped'].' already saved.';
} }

View File

@ -21,15 +21,18 @@ class WallabagV1Controller extends Controller
if ($form->isValid()) { if ($form->isValid()) {
$file = $form->get('file')->getData(); $file = $form->get('file')->getData();
$markAsRead = $form->get('mark_as_read')->getData();
$name = $this->getUser()->getId().'.json'; $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)) { if (in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $wallabag $res = $wallabag
->setUser($this->getUser()) ->setUser($this->getUser())
->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
->setMarkAsRead($markAsRead)
->import(); ->import();
$message = 'Import failed, please try again.'; $message = 'Import failed, please try again.';
if (true === $res) { if (true === $res) {
$summary = $wallabag->getSummary(); $summary = $wallabag->getSummary();
$message = 'Import summary: '.$summary['imported'].' imported, '.$summary['skipped'].' already saved.'; $message = 'Import summary: '.$summary['imported'].' imported, '.$summary['skipped'].' already saved.';

View File

@ -21,12 +21,14 @@ class WallabagV2Controller extends Controller
if ($form->isValid()) { if ($form->isValid()) {
$file = $form->get('file')->getData(); $file = $form->get('file')->getData();
$markAsRead = $form->get('mark_as_read')->getData();
$name = $this->getUser()->getId().'.json'; $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)) { if (in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $wallabag $res = $wallabag
->setUser($this->getUser()) ->setUser($this->getUser())
->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name) ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
->setMarkAsRead($markAsRead)
->import(); ->import();
$message = 'Import failed, please try again.'; $message = 'Import failed, please try again.';

View File

@ -6,6 +6,7 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
class UploadImportType extends AbstractType class UploadImportType extends AbstractType
{ {
@ -13,6 +14,10 @@ class UploadImportType extends AbstractType
{ {
$builder $builder
->add('file', FileType::class) ->add('file', FileType::class)
->add('mark_as_read', CheckboxType::class, array(
'label' => 'Mark all as read',
'required' => false,
))
->add('save', SubmitType::class) ->add('save', SubmitType::class)
; ;
} }

View File

@ -22,6 +22,7 @@ class PocketImport implements ImportInterface
private $consumerKey; private $consumerKey;
private $skippedEntries = 0; private $skippedEntries = 0;
private $importedEntries = 0; private $importedEntries = 0;
private $markAsRead;
protected $accessToken; protected $accessToken;
public function __construct(TokenStorageInterface $tokenStorage, EntityManager $em, ContentProxy $contentProxy, Config $craueConfig) public function __construct(TokenStorageInterface $tokenStorage, EntityManager $em, ContentProxy $contentProxy, Config $craueConfig)
@ -123,6 +124,26 @@ class PocketImport implements ImportInterface
return true; return true;
} }
/**
* Set whether articles must be all marked as read.
*
* @param bool $markAsRead
*/
public function setMarkAsRead($markAsRead)
{
$this->markAsRead = $markAsRead;
return $this;
}
/**
* Get whether articles must be all marked as read.
*/
public function getRead()
{
return $this->markAsRead;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -201,7 +222,7 @@ class PocketImport implements ImportInterface
$entry = $this->contentProxy->updateEntry($entry, $url); $entry = $this->contentProxy->updateEntry($entry, $url);
// 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted
if ($pocketEntry['status'] == 1) { if ($pocketEntry['status'] == 1 || $this->markAsRead) {
$entry->setArchived(true); $entry->setArchived(true);
} }

View File

@ -19,6 +19,7 @@ class WallabagV1Import implements ImportInterface
protected $skippedEntries = 0; protected $skippedEntries = 0;
protected $importedEntries = 0; protected $importedEntries = 0;
protected $filepath; protected $filepath;
protected $markAsRead;
public function __construct(EntityManager $em, ContentProxy $contentProxy) public function __construct(EntityManager $em, ContentProxy $contentProxy)
{ {
@ -120,6 +121,18 @@ class WallabagV1Import implements ImportInterface
return $this; return $this;
} }
/**
* Set whether articles must be all marked as read.
*
* @param bool $markAsRead
*/
public function setMarkAsRead($markAsRead)
{
$this->markAsRead = $markAsRead;
return $this;
}
/** /**
* @param $entries * @param $entries
*/ */
@ -160,7 +173,7 @@ class WallabagV1Import implements ImportInterface
); );
} }
$entry->setArchived($importedEntry['is_read']); $entry->setArchived($importedEntry['is_read'] || $this->markAsRead);
$entry->setStarred($importedEntry['is_fav']); $entry->setStarred($importedEntry['is_fav']);
$this->em->persist($entry); $this->em->persist($entry);

View File

@ -51,7 +51,7 @@ class WallabagV2Import extends WallabagV1Import implements ImportInterface
$entry = new Entry($this->user); $entry = new Entry($this->user);
$entry->setUrl($importedEntry['url']); $entry->setUrl($importedEntry['url']);
$entry->setTitle($importedEntry['title']); $entry->setTitle($importedEntry['title']);
$entry->setArchived($importedEntry['is_archived']); $entry->setArchived($importedEntry['is_archived'] || $this->markAsRead);
$entry->setStarred($importedEntry['is_starred']); $entry->setStarred($importedEntry['is_starred']);
$entry->setContent($importedEntry['content']); $entry->setContent($importedEntry['content']);
$entry->setReadingTime($importedEntry['reading_time']); $entry->setReadingTime($importedEntry['reading_time']);

View File

@ -19,6 +19,13 @@
<blockquote>{{ import.description|trans }}</blockquote> <blockquote>{{ import.description|trans }}</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> <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') }}"> <form method="post" action="{{ path('import_pocket_auth') }}">
<div class="row">
<div class="input-field col s6 with-checkbox">
<h6>{% trans %}Mark all as read ?{% endtrans %}</h6>
{{ form_widget(form.read) }}
<label for="form_read">{% trans %}Mark all imported entries as read{% endtrans %}</label>
</div>
</div>
<button class="btn waves-effect waves-light" type="submit" name="action"> <button class="btn waves-effect waves-light" type="submit" name="action">
{% trans %}Connect to Pocket and import data{% endtrans %} {% trans %}Connect to Pocket and import data{% endtrans %}
</button> </button>

View File

@ -22,6 +22,11 @@
<input class="file-path validate" type="text"> <input class="file-path validate" type="text">
</div> </div>
</div> </div>
<div class="input-field col s6 with-checkbox">
<h6>{% trans %}Mark all as read ?{% endtrans %}</h6>
{{ form_widget(form.mark_as_read) }}
<label for="upload_import_file_mark_as_read">{% trans %}Mark all imported entries as read{% endtrans %}</label>
</div>
</div> </div>
<div class="hidden">{{ form_rest(form) }}</div> <div class="hidden">{{ form_rest(form) }}</div>
<button class="btn waves-effect waves-light" type="submit" name="action"> <button class="btn waves-effect waves-light" type="submit" name="action">

View File

@ -58,6 +58,50 @@ class WallabagV1ControllerTest extends WallabagCoreTestCase
$this->assertContains('Import summary', $alert[0]); $this->assertContains('Import summary', $alert[0]);
} }
public function testImportWallabagWithFileAndMarkAllAsRead()
{
$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-read.json', 'wallabag-v1-read.json');
$data = array(
'upload_import_file[file]' => $file,
'upload_import_file[mark_as_read]' => 1,
);
$client->submit($form, $data);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
$content1 = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId(
'http://gilbert.pellegrom.me/recreating-the-square-slider',
$this->getLoggedInUserId()
);
$this->assertTrue($content1->isArchived());
$content2 = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId(
'https://www.wallabag.org/features/',
$this->getLoggedInUserId()
);
$this->assertTrue($content2->isArchived());
$this->assertContains('Import summary', $client->getResponse()->getContent());
}
public function testImportWallabagWithEmptyFile() public function testImportWallabagWithEmptyFile()
{ {
$this->logInAs('admin'); $this->logInAs('admin');

View File

@ -3,6 +3,7 @@
namespace Wallabag\ImportBundle\Tests\Import; namespace Wallabag\ImportBundle\Tests\Import;
use Wallabag\UserBundle\Entity\User; use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\ImportBundle\Import\PocketImport; use Wallabag\ImportBundle\Import\PocketImport;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Mock; use GuzzleHttp\Subscriber\Mock;
@ -265,9 +266,7 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
->method('getRepository') ->method('getRepository')
->willReturn($entryRepo); ->willReturn($entryRepo);
$entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry') $entry = new Entry($this->user);
->disableOriginalConstructor()
->getMock();
$this->contentProxy $this->contentProxy
->expects($this->once()) ->expects($this->once())
@ -283,6 +282,95 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(['skipped' => 1, 'imported' => 1], $pocketImport->getSummary()); $this->assertEquals(['skipped' => 1, 'imported' => 1], $pocketImport->getSummary());
} }
/**
* Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
*/
public function testImportAndMarkAllAsRead()
{
$client = new Client();
$mock = new Mock([
new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
{
"status": 1,
"list": {
"229279689": {
"item_id": "229279689",
"resolved_id": "229279689",
"given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
"given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
"favorite": "1",
"status": "1",
"resolved_title": "The Massive Ryder Cup Preview",
"resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
"excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
"is_article": "1",
"has_video": "1",
"has_image": "1",
"word_count": "3197"
},
"229279690": {
"item_id": "229279689",
"resolved_id": "229279689",
"given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview/2",
"given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
"favorite": "1",
"status": "0",
"resolved_title": "The Massive Ryder Cup Preview",
"resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
"excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
"is_article": "1",
"has_video": "0",
"has_image": "0",
"word_count": "3197"
}
}
}
')),
]);
$client->getEmitter()->attach($mock);
$pocketImport = $this->getPocketImport();
$entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
->disableOriginalConstructor()
->getMock();
$entryRepo->expects($this->exactly(2))
->method('findByUrlAndUserId')
->will($this->onConsecutiveCalls(false, false));
$this->em
->expects($this->exactly(2))
->method('getRepository')
->willReturn($entryRepo);
// check that every entry persisted are archived
$this->em
->expects($this->any())
->method('persist')
->with($this->callback(function($persistedEntry) {
return $persistedEntry->isArchived();
}));
$entry = new Entry($this->user);
$this->contentProxy
->expects($this->exactly(2))
->method('updateEntry')
->willReturn($entry);
$pocketImport->setClient($client);
$pocketImport->authorize('wunderbar_code');
$res = $pocketImport->setMarkAsRead(true)->import();
$this->assertTrue($res);
$this->assertEquals(['skipped' => 0, 'imported' => 2], $pocketImport->getSummary());
}
public function testImportBadResponse() public function testImportBadResponse()
{ {
$client = new Client(); $client = new Client();

View File

@ -81,6 +81,39 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(['skipped' => 1, 'imported' => 3], $wallabagV1Import->getSummary()); $this->assertEquals(['skipped' => 1, 'imported' => 3], $wallabagV1Import->getSummary());
} }
public function testImportAndMarkAllAsRead()
{
$wallabagV1Import = $this->getWallabagV1Import();
$wallabagV1Import->setFilepath(__DIR__.'/../fixtures/wallabag-v1-read.json');
$entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
->disableOriginalConstructor()
->getMock();
$entryRepo->expects($this->exactly(3))
->method('findByUrlAndUserId')
->will($this->onConsecutiveCalls(false, false, false));
$this->em
->expects($this->any())
->method('getRepository')
->willReturn($entryRepo);
// check that every entry persisted are archived
$this->em
->expects($this->any())
->method('persist')
->with($this->callback(function($persistedEntry) {
return $persistedEntry->isArchived();
}));
$res = $wallabagV1Import->setMarkAsRead(true)->import();
$this->assertTrue($res);
$this->assertEquals(['skipped' => 0, 'imported' => 3], $wallabagV1Import->getSummary());
}
public function testImportBadFile() public function testImportBadFile()
{ {
$wallabagV1Import = $this->getWallabagV1Import(); $wallabagV1Import = $this->getWallabagV1Import();

View File

@ -72,6 +72,39 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(['skipped' => 1, 'imported' => 2], $wallabagV2Import->getSummary()); $this->assertEquals(['skipped' => 1, 'imported' => 2], $wallabagV2Import->getSummary());
} }
public function testImportAndMarkAllAsRead()
{
$wallabagV2Import = $this->getWallabagV2Import();
$wallabagV2Import->setFilepath(__DIR__.'/../fixtures/wallabag-v2-read.json');
$entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
->disableOriginalConstructor()
->getMock();
$entryRepo->expects($this->exactly(2))
->method('findByUrlAndUserId')
->will($this->onConsecutiveCalls(false, false));
$this->em
->expects($this->any())
->method('getRepository')
->willReturn($entryRepo);
// check that every entry persisted are archived
$this->em
->expects($this->any())
->method('persist')
->with($this->callback(function($persistedEntry) {
return $persistedEntry->isArchived();
}));
$res = $wallabagV2Import->setMarkAsRead(true)->import();
$this->assertTrue($res);
$this->assertEquals(['skipped' => 0, 'imported' => 2], $wallabagV2Import->getSummary());
}
public function testImportBadFile() public function testImportBadFile()
{ {
$wallabagV1Import = $this->getWallabagV2Import(); $wallabagV1Import = $this->getWallabagV2Import();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long