aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJeremy Benoist <jeremy.benoist@gmail.com>2015-10-16 10:51:53 +0200
committerNicolas Lœuillet <nicolas.loeuillet@smile.fr>2015-11-09 16:32:48 +0100
commitadd597bad95b30dbecab3aecc8362a1ccd427976 (patch)
tree1e4976f41fb043bea41c4db4544b42458eee3500
parent03690d138792dde6405e3d2eb3c53f6572eb3c43 (diff)
downloadwallabag-add597bad95b30dbecab3aecc8362a1ccd427976.tar.gz
wallabag-add597bad95b30dbecab3aecc8362a1ccd427976.tar.zst
wallabag-add597bad95b30dbecab3aecc8362a1ccd427976.zip
Rework on export
- all export now return a `HttpFoundation\Response` - return a 404 on unsupported format - add tests
-rw-r--r--app/config/parameters.yml.dist1
-rw-r--r--app/config/tests/parameters.yml.dist.mysql1
-rw-r--r--app/config/tests/parameters.yml.dist.pgsql1
-rw-r--r--app/config/tests/parameters.yml.dist.sqlite1
-rw-r--r--composer.json2
-rw-r--r--composer.lock27
-rw-r--r--src/Wallabag/CoreBundle/Controller/ExportController.php77
-rw-r--r--src/Wallabag/CoreBundle/Helper/EntriesExport.php258
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml6
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/ExportControllerTest.php116
10 files changed, 348 insertions, 142 deletions
diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist
index 52f9bccb..b475d637 100644
--- a/app/config/parameters.yml.dist
+++ b/app/config/parameters.yml.dist
@@ -51,6 +51,7 @@ parameters:
51 export_epub: true 51 export_epub: true
52 export_mobi: true 52 export_mobi: true
53 export_pdf: true 53 export_pdf: true
54 wallabag_url: http://v2.wallabag.org
54 55
55 # default user config 56 # default user config
56 items_on_page: 12 57 items_on_page: 12
diff --git a/app/config/tests/parameters.yml.dist.mysql b/app/config/tests/parameters.yml.dist.mysql
index 03fdf5a6..5b29690c 100644
--- a/app/config/tests/parameters.yml.dist.mysql
+++ b/app/config/tests/parameters.yml.dist.mysql
@@ -51,6 +51,7 @@ parameters:
51 export_epub: true 51 export_epub: true
52 export_mobi: true 52 export_mobi: true
53 export_pdf: true 53 export_pdf: true
54 wallabag_url: http://v2.wallabag.org
54 55
55 # default user config 56 # default user config
56 items_on_page: 12 57 items_on_page: 12
diff --git a/app/config/tests/parameters.yml.dist.pgsql b/app/config/tests/parameters.yml.dist.pgsql
index 675ba6c9..efdac961 100644
--- a/app/config/tests/parameters.yml.dist.pgsql
+++ b/app/config/tests/parameters.yml.dist.pgsql
@@ -51,6 +51,7 @@ parameters:
51 export_epub: true 51 export_epub: true
52 export_mobi: true 52 export_mobi: true
53 export_pdf: true 53 export_pdf: true
54 wallabag_url: http://v2.wallabag.org
54 55
55 # default user config 56 # default user config
56 items_on_page: 12 57 items_on_page: 12
diff --git a/app/config/tests/parameters.yml.dist.sqlite b/app/config/tests/parameters.yml.dist.sqlite
index 258627af..276d1147 100644
--- a/app/config/tests/parameters.yml.dist.sqlite
+++ b/app/config/tests/parameters.yml.dist.sqlite
@@ -51,6 +51,7 @@ parameters:
51 export_epub: true 51 export_epub: true
52 export_mobi: true 52 export_mobi: true
53 export_pdf: true 53 export_pdf: true
54 wallabag_url: http://v2.wallabag.org
54 55
55 # default user config 56 # default user config
56 items_on_page: 12 57 items_on_page: 12
diff --git a/composer.json b/composer.json
index a25e143a..b6a9c854 100644
--- a/composer.json
+++ b/composer.json
@@ -57,7 +57,7 @@
57 "friendsofsymfony/oauth-server-bundle": "^1.4@dev", 57 "friendsofsymfony/oauth-server-bundle": "^1.4@dev",
58 "scheb/two-factor-bundle": "~1.4", 58 "scheb/two-factor-bundle": "~1.4",
59 "grandt/phpepub": "~4.0", 59 "grandt/phpepub": "~4.0",
60 "wallabag/phpMobi": "~1.0.0" 60 "wallabag/php-mobi": "~1.0.0"
61 }, 61 },
62 "require-dev": { 62 "require-dev": {
63 "doctrine/doctrine-fixtures-bundle": "~2.2.0", 63 "doctrine/doctrine-fixtures-bundle": "~2.2.0",
diff --git a/composer.lock b/composer.lock
index c634abbd..ae53a6d6 100644
--- a/composer.lock
+++ b/composer.lock
@@ -3796,41 +3796,52 @@
3796 "time": "2015-11-05 12:49:06" 3796 "time": "2015-11-05 12:49:06"
3797 }, 3797 },
3798 { 3798 {
3799 "name": "wallabag/phpMobi", 3799 "name": "wallabag/php-mobi",
3800 "version": "1.0.0", 3800 "version": "1.0.1",
3801 "source": { 3801 "source": {
3802 "type": "git", 3802 "type": "git",
3803 "url": "https://github.com/wallabag/phpMobi.git", 3803 "url": "https://github.com/wallabag/phpMobi.git",
3804 "reference": "5137696542f08f8e6a0603c01970c6d3eca9873d" 3804 "reference": "1cd7d022fe6be838535d6bba917d19cc48dcf487"
3805 }, 3805 },
3806 "dist": { 3806 "dist": {
3807 "type": "zip", 3807 "type": "zip",
3808 "url": "https://api.github.com/repos/wallabag/phpMobi/zipball/5137696542f08f8e6a0603c01970c6d3eca9873d", 3808 "url": "https://api.github.com/repos/wallabag/phpMobi/zipball/1cd7d022fe6be838535d6bba917d19cc48dcf487",
3809 "reference": "5137696542f08f8e6a0603c01970c6d3eca9873d", 3809 "reference": "1cd7d022fe6be838535d6bba917d19cc48dcf487",
3810 "shasum": "" 3810 "shasum": ""
3811 }, 3811 },
3812 "require": { 3812 "require": {
3813 "php": ">=5.3.0" 3813 "php": ">=5.3.0"
3814 }, 3814 },
3815 "replace": {
3816 "wallabag/phpmobi": "*"
3817 },
3815 "type": "library", 3818 "type": "library",
3816 "autoload": { 3819 "autoload": {
3817 "files": [ 3820 "files": [
3818 "MOBIClass/MOBI.php" 3821 "MOBIClass/MOBI.php"
3819 ] 3822 ]
3820 }, 3823 },
3824 "license": [
3825 "Apache-2.0"
3826 ],
3821 "authors": [ 3827 "authors": [
3822 { 3828 {
3829 "name": "Sander Kromwijk",
3830 "email": "s.kromwijk@gmail.co",
3831 "role": "Original developer"
3832 },
3833 {
3823 "name": "Nicolas Lœuillet", 3834 "name": "Nicolas Lœuillet",
3824 "email": "nicolas@loeuillet.org", 3835 "email": "nicolas@loeuillet.org",
3825 "homepage": "http://www.cdetc.fr" 3836 "homepage": "http://www.cdetc.fr"
3826 } 3837 }
3827 ], 3838 ],
3828 "description": "An experimental Mobipocket file creator in PHP.", 3839 "description": "A Mobipocket file (.mobi) creator in PHP.",
3829 "homepage": "https://github.com/wallabag/phpMobi", 3840 "homepage": "https://github.com/wallabag/phpMobi",
3830 "support": { 3841 "support": {
3831 "source": "https://github.com/wallabag/phpMobi/tree/1.0.0" 3842 "source": "https://github.com/wallabag/phpMobi/tree/1.0.1"
3832 }, 3843 },
3833 "time": "2015-01-19 12:43:17" 3844 "time": "2015-10-16 08:42:42"
3834 }, 3845 },
3835 { 3846 {
3836 "name": "willdurand/hateoas", 3847 "name": "willdurand/hateoas",
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php
index 123e491a..dd3cb7ca 100644
--- a/src/Wallabag/CoreBundle/Controller/ExportController.php
+++ b/src/Wallabag/CoreBundle/Controller/ExportController.php
@@ -4,62 +4,55 @@ namespace Wallabag\CoreBundle\Controller;
4 4
5use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 5use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
6use Symfony\Bundle\FrameworkBundle\Controller\Controller; 6use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
7use Wallabag\CoreBundle\Entity\Entry; 8use Wallabag\CoreBundle\Entity\Entry;
8use Wallabag\CoreBundle\Helper\EntriesExport;
9 9
10class ExportController extends Controller 10class ExportController extends Controller
11{ 11{
12 /** 12 /**
13 * Gets all entries for current user. 13 * Gets one entry content.
14 * 14 *
15 * @Route("/export/{category}.{format}", name="ebook", requirements={ 15 * @param Entry $entry
16 * "_format": "epub|mobi|pdf|json|xml|txt|csv" 16 *
17 * }) 17 * @Route("/export/{id}.{format}", requirements={"id" = "\d+"}, name="export_entry")
18 */ 18 */
19 public function getEntriesAction($format, $category) 19 public function downloadEntryAction(Entry $entry, $format)
20 { 20 {
21 $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); 21 try {
22 switch ($category) { 22 return $this->get('wallabag_core.helper.entries_export')
23 case 'all': 23 ->setEntries($entry)
24 $method = 'All'; 24 ->updateTitle('entry')
25 break; 25 ->exportAs($format);
26 26 } catch (\InvalidArgumentException $e) {
27 case 'unread': 27 throw new NotFoundHttpException($e->getMessage());
28 $method = 'Unread';
29 break;
30
31 case 'starred':
32 $method = 'Starred';
33 break;
34
35 case 'archive':
36 $method = 'Archive';
37 break;
38
39 default:
40 break;
41 } 28 }
42
43 $methodBuilder = 'getBuilderFor'.$method.'ByUser';
44 $qb = $repository->$methodBuilder($this->getUser()->getId());
45 $entries = $qb->getQuery()->getResult();
46
47 $export = new EntriesExport($entries);
48 $export->setMethod($method);
49 $export->exportAs($format);
50 } 29 }
51 30
52 /** 31 /**
53 * Gets one entry content. 32 * Export all entries for current user.
54 *
55 * @param Entry $entry
56 * 33 *
57 * @Route("/export/id/{id}.{format}", requirements={"id" = "\d+"}, name="ebook_entry") 34 * @Route("/export/{category}.{format}", name="export_entries", requirements={
35 * "_format": "epub|mobi|pdf|json|xml|txt|csv",
36 * "category": "all|unread|starred|archive"
37 * })
58 */ 38 */
59 public function getEntryAction(Entry $entry, $format) 39 public function downloadEntriesAction($format, $category)
60 { 40 {
61 $export = new EntriesExport(array($entry)); 41 $method = ucfirst($category);
62 $export->setMethod('entry'); 42 $methodBuilder = 'getBuilderFor'.$method.'ByUser';
63 $export->exportAs($format); 43 $entries = $this->getDoctrine()
44 ->getRepository('WallabagCoreBundle:Entry')
45 ->$methodBuilder($this->getUser()->getId())
46 ->getQuery()
47 ->getResult();
48
49 try {
50 return $this->get('wallabag_core.helper.entries_export')
51 ->setEntries($entries)
52 ->updateTitle($method)
53 ->exportAs($format);
54 } catch (\InvalidArgumentException $e) {
55 throw new NotFoundHttpException($e->getMessage());
56 }
64 } 57 }
65} 58}
diff --git a/src/Wallabag/CoreBundle/Helper/EntriesExport.php b/src/Wallabag/CoreBundle/Helper/EntriesExport.php
index fad0bb97..806319b1 100644
--- a/src/Wallabag/CoreBundle/Helper/EntriesExport.php
+++ b/src/Wallabag/CoreBundle/Helper/EntriesExport.php
@@ -4,27 +4,51 @@ namespace Wallabag\CoreBundle\Helper;
4 4
5use PHPePub\Core\EPub; 5use PHPePub\Core\EPub;
6use PHPePub\Core\Structure\OPF\DublinCore; 6use PHPePub\Core\Structure\OPF\DublinCore;
7use Symfony\Component\HttpFoundation\Response;
7 8
8class EntriesExport 9class EntriesExport
9{ 10{
10 private $format; 11 private $wallabagUrl;
11 private $method; 12 private $logoPath;
12 private $title; 13 private $title = '';
13 private $entries; 14 private $entries = array();
14 private $authors = array('wallabag'); 15 private $authors = array('wallabag');
15 private $language; 16 private $language = '';
16 private $tags; 17 private $tags = array();
18 private $footerTemplate = '<div style="text-align:center;">
19 <p>Produced by wallabag with %EXPORT_METHOD%</p>
20 <p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p>
21 </div';
17 22
18 public function __construct($entries) 23 /**
24 * @param string $wallabagUrl Wallabag instance url
25 * @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE
26 */
27 public function __construct($wallabagUrl, $logoPath)
19 { 28 {
29 $this->wallabagUrl = $wallabagUrl;
30 $this->logoPath = $logoPath;
31 }
32
33 /**
34 * Define entries.
35 *
36 * @param array|Entry $entries An array of entries or one entry
37 */
38 public function setEntries($entries)
39 {
40 if (!is_array($entries)) {
41 $this->language = $entries->getLanguage();
42 $entries = array($entries);
43 }
44
20 $this->entries = $entries; 45 $this->entries = $entries;
21 46
22 foreach ($entries as $entry) { 47 foreach ($entries as $entry) {
23 $this->tags[] = $entry->getTags(); 48 $this->tags[] = $entry->getTags();
24 } 49 }
25 if (count($entries) === 1) { 50
26 $this->language = $entries[0]->getLanguage(); 51 return $this;
27 }
28 } 52 }
29 53
30 /** 54 /**
@@ -32,29 +56,15 @@ class EntriesExport
32 * 56 *
33 * @param string $method Method to get articles 57 * @param string $method Method to get articles
34 */ 58 */
35 public function setMethod($method) 59 public function updateTitle($method)
36 { 60 {
37 $this->method = $method; 61 $this->title = $method.' articles';
38 62
39 switch ($this->method) { 63 if ('entry' === $method) {
40 case 'All': 64 $this->title = $this->entries[0]->getTitle();
41 $this->title = 'All Articles';
42 break;
43 case 'Unread':
44 $this->title = 'Unread articles';
45 break;
46 case 'Starred':
47 $this->title = 'Starred articles';
48 break;
49 case 'Archive':
50 $this->title = 'Archived articles';
51 break;
52 case 'entry':
53 $this->title = $this->entries[0]->getTitle();
54 break;
55 default:
56 break;
57 } 65 }
66
67 return $this;
58 } 68 }
59 69
60 /** 70 /**
@@ -64,30 +74,26 @@ class EntriesExport
64 */ 74 */
65 public function exportAs($format) 75 public function exportAs($format)
66 { 76 {
67 $this->format = $format; 77 switch ($format) {
68
69 switch ($this->format) {
70 case 'epub': 78 case 'epub':
71 $this->produceEpub(); 79 return $this->produceEpub();
72 break;
73 80
74 case 'mobi': 81 case 'mobi':
75 $this->produceMobi(); 82 return $this->produceMobi();
76 break;
77 83
78 case 'pdf': 84 case 'pdf':
79 $this->producePDF(); 85 return $this->producePDF();
80 break;
81 86
82 case 'csv': 87 case 'csv':
83 $this->produceCSV(); 88 return $this->produceCSV();
84 break;
85
86 default:
87 break;
88 } 89 }
90
91 throw new \InvalidArgumentException(sprintf('The format "%s" is not yet supported.', $format));
89 } 92 }
90 93
94 /**
95 * Use PHPePub to dump a .epub file.
96 */
91 private function produceEpub() 97 private function produceEpub()
92 { 98 {
93 /* 99 /*
@@ -98,7 +104,7 @@ class EntriesExport
98 ."<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" 104 ."<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
99 .'<head>' 105 .'<head>'
100 ."<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n" 106 ."<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
101 .'<title>'._('wallabag articles book')."</title>\n" 107 ."<title>wallabag articles book</title>\n"
102 ."</head>\n" 108 ."</head>\n"
103 ."<body>\n"; 109 ."<body>\n";
104 110
@@ -111,17 +117,21 @@ class EntriesExport
111 */ 117 */
112 118
113 $book->setTitle($this->title); 119 $book->setTitle($this->title);
114 $book->setIdentifier($this->title, EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID. 120 // Could also be the ISBN number, prefered for published books, or a UUID.
115 $book->setLanguage($this->language); // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc. 121 $book->setIdentifier($this->title, EPub::IDENTIFIER_URI);
116 $book->setDescription(_('Some articles saved on my wallabag')); 122 // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc.
123 $book->setLanguage($this->language);
124 $book->setDescription('Some articles saved on my wallabag');
117 125
118 foreach ($this->authors as $author) { 126 foreach ($this->authors as $author) {
119 $book->setAuthor($author, $author); 127 $book->setAuthor($author, $author);
120 } 128 }
121 129
122 $book->setPublisher('wallabag', 'wallabag'); // I hope this is a non existant address :) 130 // I hope this is a non existant address :)
123 $book->setDate(time()); // Strictly not needed as the book date defaults to time(). 131 $book->setPublisher('wallabag', 'wallabag');
124 $book->setSourceURL("http://$_SERVER[HTTP_HOST]"); 132 // Strictly not needed as the book date defaults to time().
133 $book->setDate(time());
134 $book->setSourceURL($this->wallabagUrl);
125 135
126 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'PHP'); 136 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'PHP');
127 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'wallabag'); 137 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'wallabag');
@@ -129,12 +139,11 @@ class EntriesExport
129 /* 139 /*
130 * Front page 140 * Front page
131 */ 141 */
142 if (file_exists($this->logoPath)) {
143 $book->setCoverImage('Cover.png', file_get_contents($this->logoPath), 'image/png');
144 }
132 145
133 $book->setCoverImage('Cover.png', file_get_contents('themes/_global/img/appicon/apple-touch-icon-152.png'), 'image/png'); 146 $book->addChapter('Notices', 'Cover2.html', $content_start.$this->getExportInformation('PHPePub').$bookEnd);
134
135 $cover = $content_start.'<div style="text-align:center;"><p>'._('Produced by wallabag with PHPePub').'</p><p>'._('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.').'</p></div>'.$bookEnd;
136
137 $book->addChapter('Notices', 'Cover2.html', $cover);
138 147
139 $book->buildTOC(); 148 $book->buildTOC();
140 149
@@ -142,18 +151,31 @@ class EntriesExport
142 * Adding actual entries 151 * Adding actual entries
143 */ 152 */
144 153
145 foreach ($this->entries as $entry) { //set tags as subjects 154 // set tags as subjects
146 foreach ($this->tags as $tag) { 155 foreach ($this->entries as $entry) {
147 $book->setSubject($tag['value']); 156 foreach ($this->tags as $tag) {
148 } 157 $book->setSubject($tag['value']);
158 }
149 159
150 $chapter = $content_start.$entry->getContent().$bookEnd; 160 $chapter = $content_start.$entry->getContent().$bookEnd;
151 $book->addChapter($entry->getTitle(), htmlspecialchars($entry->getTitle()).'.html', $chapter, true, EPub::EXTERNAL_REF_ADD); 161 $book->addChapter($entry->getTitle(), htmlspecialchars($entry->getTitle()).'.html', $chapter, true, EPub::EXTERNAL_REF_ADD);
152 } 162 }
153 $book->finalize(); 163
154 $book->sendBook($this->title); 164 return Response::create(
165 $book->getBook(),
166 200,
167 array(
168 'Content-Description' => 'File Transfer',
169 'Content-type' => 'application/epub+zip',
170 'Content-Disposition' => 'attachment; filename="'.$this->title.'.epub"',
171 'Content-Transfer-Encoding' => 'binary',
172 )
173 )->send();
155 } 174 }
156 175
176 /**
177 * Use PHPMobi to dump a .mobi file.
178 */
157 private function produceMobi() 179 private function produceMobi()
158 { 180 {
159 $mobi = new \MOBI(); 181 $mobi = new \MOBI();
@@ -162,7 +184,6 @@ class EntriesExport
162 /* 184 /*
163 * Book metadata 185 * Book metadata
164 */ 186 */
165
166 $content->set('title', $this->title); 187 $content->set('title', $this->title);
167 $content->set('author', implode($this->authors)); 188 $content->set('author', implode($this->authors));
168 $content->set('subject', $this->title); 189 $content->set('subject', $this->title);
@@ -170,15 +191,15 @@ class EntriesExport
170 /* 191 /*
171 * Front page 192 * Front page
172 */ 193 */
173 194 $content->appendParagraph($this->getExportInformation('PHPMobi'));
174 $content->appendParagraph('<div style="text-align:center;" ><p>'._('Produced by wallabag with PHPMobi').'</p><p>'._('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.').'</p></div>'); 195 if (file_exists($this->logoPath)) {
175 $content->appendImage(imagecreatefrompng('themes/_global/img/appicon/apple-touch-icon-152.png')); 196 $content->appendImage(imagecreatefrompng($this->logoPath));
197 }
176 $content->appendPageBreak(); 198 $content->appendPageBreak();
177 199
178 /* 200 /*
179 * Adding actual entries 201 * Adding actual entries
180 */ 202 */
181
182 foreach ($this->entries as $entry) { 203 foreach ($this->entries as $entry) {
183 $content->appendChapterTitle($entry->getTitle()); 204 $content->appendChapterTitle($entry->getTitle());
184 $content->appendParagraph($entry->getContent()); 205 $content->appendParagraph($entry->getContent());
@@ -189,10 +210,22 @@ class EntriesExport
189 // the browser inside Kindle Devices doesn't likes special caracters either, we limit to A-z/0-9 210 // the browser inside Kindle Devices doesn't likes special caracters either, we limit to A-z/0-9
190 $this->title = preg_replace('/[^A-Za-z0-9\-]/', '', $this->title); 211 $this->title = preg_replace('/[^A-Za-z0-9\-]/', '', $this->title);
191 212
192 // we offer file to download 213 return Response::create(
193 $mobi->download($this->title.'.mobi'); 214 $mobi->toString(),
215 200,
216 array(
217 'Accept-Ranges' => 'bytes',
218 'Content-Description' => 'File Transfer',
219 'Content-type' => 'application/x-mobipocket-ebook',
220 'Content-Disposition' => 'attachment; filename="'.$this->title.'.mobi"',
221 'Content-Transfer-Encoding' => 'binary',
222 )
223 )->send();
194 } 224 }
195 225
226 /**
227 * Use TCPDF to dump a .pdf file.
228 */
196 private function producePDF() 229 private function producePDF()
197 { 230 {
198 $pdf = new \TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); 231 $pdf = new \TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
@@ -200,7 +233,6 @@ class EntriesExport
200 /* 233 /*
201 * Book metadata 234 * Book metadata
202 */ 235 */
203
204 $pdf->SetCreator(PDF_CREATOR); 236 $pdf->SetCreator(PDF_CREATOR);
205 $pdf->SetAuthor('wallabag'); 237 $pdf->SetAuthor('wallabag');
206 $pdf->SetTitle($this->title); 238 $pdf->SetTitle($this->title);
@@ -210,19 +242,14 @@ class EntriesExport
210 /* 242 /*
211 * Front page 243 * Front page
212 */ 244 */
213
214 $pdf->AddPage(); 245 $pdf->AddPage();
215 $intro = '<h1>'.$this->title.'</h1><div style="text-align:center;" > 246 $intro = '<h1>'.$this->title.'</h1>'.$this->getExportInformation('tcpdf');
216 <p>'._('Produced by wallabag with tcpdf').'</p>
217 <p>'._('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.').'</p>
218 <img src="themes/_global/img/appicon/apple-touch-icon-152.png" /></div>';
219 247
220 $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true); 248 $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true);
221 249
222 /* 250 /*
223 * Adding actual entries 251 * Adding actual entries
224 */ 252 */
225
226 foreach ($this->entries as $entry) { 253 foreach ($this->entries as $entry) {
227 foreach ($this->tags as $tag) { 254 foreach ($this->tags as $tag) {
228 $pdf->SetKeywords($tag['value']); 255 $pdf->SetKeywords($tag['value']);
@@ -231,33 +258,82 @@ class EntriesExport
231 $pdf->AddPage(); 258 $pdf->AddPage();
232 $html = '<h1>'.$entry->getTitle().'</h1>'; 259 $html = '<h1>'.$entry->getTitle().'</h1>';
233 $html .= $entry->getContent(); 260 $html .= $entry->getContent();
261
234 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); 262 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
235 } 263 }
236 264
237 // set image scale factor 265 // set image scale factor
238 $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); 266 $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
239 267
240 $pdf->Output($this->title.'.pdf', 'D'); 268 return Response::create(
269 $pdf->Output('', 'S'),
270 200,
271 array(
272 'Content-Description' => 'File Transfer',
273 'Content-type' => 'application/pdf',
274 'Content-Disposition' => 'attachment; filename="'.$this->title.'.pdf"',
275 'Content-Transfer-Encoding' => 'binary',
276 )
277 )->send();
241 } 278 }
242 279
280 /**
281 * Inspired from CsvFileDumper.
282 */
243 private function produceCSV() 283 private function produceCSV()
244 { 284 {
245 header('Content-type: application/csv'); 285 $delimiter = ';';
246 header('Content-Disposition: attachment; filename="'.$this->title.'.csv"'); 286 $enclosure = '"';
247 header('Content-Transfer-Encoding: UTF-8'); 287 $handle = fopen('php://memory', 'rb+');
248 288
249 $output = fopen('php://output', 'a'); 289 fputcsv($handle, array('Title', 'URL', 'Content', 'Tags', 'MIME Type', 'Language'), $delimiter, $enclosure);
250 290
251 fputcsv($output, array('Title', 'URL', 'Content', 'Tags', 'MIME Type', 'Language'));
252 foreach ($this->entries as $entry) { 291 foreach ($this->entries as $entry) {
253 fputcsv($output, array($entry->getTitle(), 292 fputcsv(
254 $entry->getURL(), 293 $handle,
255 $entry->getContent(), 294 array(
256 implode(', ', $entry->getTags()->toArray()), 295 $entry->getTitle(),
257 $entry->getMimetype(), 296 $entry->getURL(),
258 $entry->getLanguage(), )); 297 $entry->getContent(),
298 implode(', ', $entry->getTags()->toArray()),
299 $entry->getMimetype(),
300 $entry->getLanguage(),
301 ),
302 $delimiter,
303 $enclosure
304 );
305 }
306
307 rewind($handle);
308 $output = stream_get_contents($handle);
309 fclose($handle);
310
311 return Response::create(
312 $output,
313 200,
314 array(
315 'Content-type' => 'application/csv',
316 'Content-Disposition' => 'attachment; filename="'.$this->title.'.csv"',
317 'Content-Transfer-Encoding' => 'UTF-8',
318 )
319 )->send();
320 }
321
322 /**
323 * Return a kind of footer / information for the epub.
324 *
325 * @param string $type Generator of the export, can be: tdpdf, PHPePub, PHPMobi
326 *
327 * @return string
328 */
329 private function getExportInformation($type)
330 {
331 $info = str_replace('%EXPORT_METHOD%', $type, $this->footerTemplate);
332
333 if ('tcpdf' === $type) {
334 return str_replace('%IMAGE%', '<img src="'.$this->logoPath.'" />', $info);
259 } 335 }
260 fclose($output); 336
261 exit(); 337 return str_replace('%IMAGE%', '', $info);
262 } 338 }
263} 339}
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index 65c2c8d8..8e21b052 100644
--- a/src/Wallabag/CoreBundle/Resources/config/services.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/services.yml
@@ -64,3 +64,9 @@ services:
64 - %language% 64 - %language%
65 tags: 65 tags:
66 - { name: kernel.event_subscriber } 66 - { name: kernel.event_subscriber }
67
68 wallabag_core.helper.entries_export:
69 class: Wallabag\CoreBundle\Helper\EntriesExport
70 arguments:
71 - %wallabag_url%
72 - src/Wallabag/CoreBundle/Resources/views/themes/_global/public/img/appicon/apple-touch-icon-152.png
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ExportControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ExportControllerTest.php
new file mode 100644
index 00000000..19d5f01d
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Tests/Controller/ExportControllerTest.php
@@ -0,0 +1,116 @@
1<?php
2
3namespace Wallabag\CoreBundle\Tests\Controller;
4
5use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
6
7class ExportControllerTest extends WallabagCoreTestCase
8{
9 public function testLogin()
10 {
11 $client = $this->getClient();
12
13 $client->request('GET', '/export/unread.csv');
14
15 $this->assertEquals(302, $client->getResponse()->getStatusCode());
16 $this->assertContains('login', $client->getResponse()->headers->get('location'));
17 }
18
19 public function testUnknownCategoryExport()
20 {
21 $this->logInAs('admin');
22 $client = $this->getClient();
23
24 $crawler = $client->request('GET', '/export/awesomeness.epub');
25
26 $this->assertEquals(404, $client->getResponse()->getStatusCode());
27 }
28
29 public function testUnknownFormatExport()
30 {
31 $this->logInAs('admin');
32 $client = $this->getClient();
33
34 $crawler = $client->request('GET', '/export/unread.xslx');
35
36 $this->assertEquals(404, $client->getResponse()->getStatusCode());
37 }
38
39 public function testEpubExport()
40 {
41 $this->logInAs('admin');
42 $client = $this->getClient();
43
44 ob_start();
45 $crawler = $client->request('GET', '/export/archive.epub');
46 ob_end_clean();
47
48 $this->assertEquals(200, $client->getResponse()->getStatusCode());
49
50 $headers = $client->getResponse()->headers;
51 $this->assertEquals('application/epub+zip', $headers->get('content-type'));
52 $this->assertEquals('attachment; filename="Archive articles.epub"', $headers->get('content-disposition'));
53 $this->assertEquals('binary', $headers->get('content-transfer-encoding'));
54 }
55
56 public function testMobiExport()
57 {
58 $this->logInAs('admin');
59 $client = $this->getClient();
60
61 $content = $client->getContainer()
62 ->get('doctrine.orm.entity_manager')
63 ->getRepository('WallabagCoreBundle:Entry')
64 ->findOneByUsernameAndNotArchived('admin');
65
66 ob_start();
67 $crawler = $client->request('GET', '/export/'.$content->getId().'.mobi');
68 ob_end_clean();
69
70 $this->assertEquals(200, $client->getResponse()->getStatusCode());
71
72 $headers = $client->getResponse()->headers;
73 $this->assertEquals('application/x-mobipocket-ebook', $headers->get('content-type'));
74 $this->assertEquals('attachment; filename="testtitleentry1.mobi"', $headers->get('content-disposition'));
75 $this->assertEquals('binary', $headers->get('content-transfer-encoding'));
76 }
77
78 public function testPdfExport()
79 {
80 $this->logInAs('admin');
81 $client = $this->getClient();
82
83 ob_start();
84 $crawler = $client->request('GET', '/export/all.pdf');
85 ob_end_clean();
86
87 $this->assertEquals(200, $client->getResponse()->getStatusCode());
88
89 $headers = $client->getResponse()->headers;
90 $this->assertEquals('application/pdf', $headers->get('content-type'));
91 $this->assertEquals('attachment; filename="All articles.pdf"', $headers->get('content-disposition'));
92 $this->assertEquals('binary', $headers->get('content-transfer-encoding'));
93 }
94
95 public function testCsvExport()
96 {
97 $this->logInAs('admin');
98 $client = $this->getClient();
99
100 ob_start();
101 $crawler = $client->request('GET', '/export/unread.csv');
102 ob_end_clean();
103
104 $this->assertEquals(200, $client->getResponse()->getStatusCode());
105
106 $headers = $client->getResponse()->headers;
107 $this->assertEquals('application/csv', $headers->get('content-type'));
108 $this->assertEquals('attachment; filename="Unread articles.csv"', $headers->get('content-disposition'));
109 $this->assertEquals('UTF-8', $headers->get('content-transfer-encoding'));
110
111 $csv = str_getcsv($client->getResponse()->getContent(), "\n");
112
113 $this->assertGreaterThan(1, $csv);
114 $this->assertEquals('Title;URL;Content;Tags;"MIME Type";Language', $csv[0]);
115 }
116}