]> git.immae.eu Git - github/wallabag/wallabag.git/blame - src/Wallabag/CoreBundle/Helper/EntriesExport.php
Improve composer.json
[github/wallabag/wallabag.git] / src / Wallabag / CoreBundle / Helper / EntriesExport.php
CommitLineData
03690d13
TC
1<?php
2
3namespace Wallabag\CoreBundle\Helper;
4
5use PHPePub\Core\EPub;
6use PHPePub\Core\Structure\OPF\DublinCore;
add597ba 7use Symfony\Component\HttpFoundation\Response;
268e9e72
TC
8use JMS\Serializer;
9use JMS\Serializer\SerializerBuilder;
10use JMS\Serializer\SerializationContext;
03690d13 11
cceca9ea
JB
12/**
13 * This class doesn't have unit test BUT it's fully covered by a functional test with ExportControllerTest.
14 */
03690d13
TC
15class EntriesExport
16{
add597ba
JB
17 private $wallabagUrl;
18 private $logoPath;
19 private $title = '';
20 private $entries = array();
03690d13 21 private $authors = array('wallabag');
add597ba
JB
22 private $language = '';
23 private $tags = array();
24 private $footerTemplate = '<div style="text-align:center;">
25 <p>Produced by wallabag with %EXPORT_METHOD%</p>
26 <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>
27 </div';
03690d13 28
add597ba
JB
29 /**
30 * @param string $wallabagUrl Wallabag instance url
31 * @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE
32 */
33 public function __construct($wallabagUrl, $logoPath)
03690d13 34 {
add597ba
JB
35 $this->wallabagUrl = $wallabagUrl;
36 $this->logoPath = $logoPath;
37 }
38
39 /**
40 * Define entries.
41 *
42 * @param array|Entry $entries An array of entries or one entry
43 */
44 public function setEntries($entries)
45 {
46 if (!is_array($entries)) {
47 $this->language = $entries->getLanguage();
48 $entries = array($entries);
49 }
50
03690d13
TC
51 $this->entries = $entries;
52
53 foreach ($entries as $entry) {
54 $this->tags[] = $entry->getTags();
55 }
add597ba
JB
56
57 return $this;
03690d13
TC
58 }
59
60 /**
61 * Sets the category of which we want to get articles, or just one entry.
62 *
63 * @param string $method Method to get articles
64 */
add597ba 65 public function updateTitle($method)
03690d13 66 {
add597ba
JB
67 $this->title = $method.' articles';
68
69 if ('entry' === $method) {
70 $this->title = $this->entries[0]->getTitle();
03690d13 71 }
add597ba
JB
72
73 return $this;
03690d13
TC
74 }
75
76 /**
77 * Sets the output format.
78 *
79 * @param string $format
80 */
81 public function exportAs($format)
82 {
add597ba 83 switch ($format) {
03690d13 84 case 'epub':
add597ba 85 return $this->produceEpub();
03690d13
TC
86
87 case 'mobi':
add597ba 88 return $this->produceMobi();
03690d13
TC
89
90 case 'pdf':
add597ba 91 return $this->producePDF();
03690d13
TC
92
93 case 'csv':
add597ba 94 return $this->produceCSV();
b3cc1a14
TC
95
96 case 'json':
97 return $this->produceJSON();
98
99 case 'xml':
100 return $this->produceXML();
03690d13 101 }
add597ba
JB
102
103 throw new \InvalidArgumentException(sprintf('The format "%s" is not yet supported.', $format));
03690d13
TC
104 }
105
add597ba
JB
106 /**
107 * Use PHPePub to dump a .epub file.
108 */
03690d13
TC
109 private function produceEpub()
110 {
111 /*
112 * Start and End of the book
113 */
114 $content_start =
115 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
116 ."<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
117 .'<head>'
118 ."<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
add597ba 119 ."<title>wallabag articles book</title>\n"
03690d13
TC
120 ."</head>\n"
121 ."<body>\n";
122
123 $bookEnd = "</body>\n</html>\n";
124
125 $book = new EPub(EPub::BOOK_VERSION_EPUB3);
126
127 /*
128 * Book metadata
129 */
130
131 $book->setTitle($this->title);
add597ba
JB
132 // Could also be the ISBN number, prefered for published books, or a UUID.
133 $book->setIdentifier($this->title, EPub::IDENTIFIER_URI);
134 // 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.
135 $book->setLanguage($this->language);
136 $book->setDescription('Some articles saved on my wallabag');
03690d13
TC
137
138 foreach ($this->authors as $author) {
139 $book->setAuthor($author, $author);
140 }
141
add597ba
JB
142 // I hope this is a non existant address :)
143 $book->setPublisher('wallabag', 'wallabag');
144 // Strictly not needed as the book date defaults to time().
145 $book->setDate(time());
146 $book->setSourceURL($this->wallabagUrl);
03690d13
TC
147
148 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'PHP');
149 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'wallabag');
150
151 /*
152 * Front page
153 */
add597ba
JB
154 if (file_exists($this->logoPath)) {
155 $book->setCoverImage('Cover.png', file_get_contents($this->logoPath), 'image/png');
156 }
03690d13 157
add597ba 158 $book->addChapter('Notices', 'Cover2.html', $content_start.$this->getExportInformation('PHPePub').$bookEnd);
03690d13
TC
159
160 $book->buildTOC();
161
162 /*
163 * Adding actual entries
164 */
165
add597ba
JB
166 // set tags as subjects
167 foreach ($this->entries as $entry) {
168 foreach ($this->tags as $tag) {
169 $book->setSubject($tag['value']);
170 }
03690d13
TC
171
172 $chapter = $content_start.$entry->getContent().$bookEnd;
173 $book->addChapter($entry->getTitle(), htmlspecialchars($entry->getTitle()).'.html', $chapter, true, EPub::EXTERNAL_REF_ADD);
174 }
add597ba
JB
175
176 return Response::create(
177 $book->getBook(),
178 200,
179 array(
180 'Content-Description' => 'File Transfer',
181 'Content-type' => 'application/epub+zip',
182 'Content-Disposition' => 'attachment; filename="'.$this->title.'.epub"',
183 'Content-Transfer-Encoding' => 'binary',
184 )
185 )->send();
03690d13
TC
186 }
187
add597ba
JB
188 /**
189 * Use PHPMobi to dump a .mobi file.
190 */
03690d13
TC
191 private function produceMobi()
192 {
193 $mobi = new \MOBI();
194 $content = new \MOBIFile();
195
196 /*
197 * Book metadata
198 */
03690d13
TC
199 $content->set('title', $this->title);
200 $content->set('author', implode($this->authors));
201 $content->set('subject', $this->title);
202
203 /*
204 * Front page
205 */
add597ba
JB
206 $content->appendParagraph($this->getExportInformation('PHPMobi'));
207 if (file_exists($this->logoPath)) {
208 $content->appendImage(imagecreatefrompng($this->logoPath));
209 }
03690d13
TC
210 $content->appendPageBreak();
211
212 /*
213 * Adding actual entries
214 */
03690d13
TC
215 foreach ($this->entries as $entry) {
216 $content->appendChapterTitle($entry->getTitle());
217 $content->appendParagraph($entry->getContent());
218 $content->appendPageBreak();
219 }
220 $mobi->setContentProvider($content);
221
222 // the browser inside Kindle Devices doesn't likes special caracters either, we limit to A-z/0-9
223 $this->title = preg_replace('/[^A-Za-z0-9\-]/', '', $this->title);
224
add597ba
JB
225 return Response::create(
226 $mobi->toString(),
227 200,
228 array(
229 'Accept-Ranges' => 'bytes',
230 'Content-Description' => 'File Transfer',
231 'Content-type' => 'application/x-mobipocket-ebook',
232 'Content-Disposition' => 'attachment; filename="'.$this->title.'.mobi"',
233 'Content-Transfer-Encoding' => 'binary',
234 )
235 )->send();
03690d13
TC
236 }
237
add597ba
JB
238 /**
239 * Use TCPDF to dump a .pdf file.
240 */
03690d13
TC
241 private function producePDF()
242 {
243 $pdf = new \TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
244
245 /*
246 * Book metadata
247 */
03690d13
TC
248 $pdf->SetCreator(PDF_CREATOR);
249 $pdf->SetAuthor('wallabag');
250 $pdf->SetTitle($this->title);
251 $pdf->SetSubject('Articles via wallabag');
252 $pdf->SetKeywords('wallabag');
253
254 /*
255 * Front page
256 */
03690d13 257 $pdf->AddPage();
add597ba 258 $intro = '<h1>'.$this->title.'</h1>'.$this->getExportInformation('tcpdf');
03690d13
TC
259
260 $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true);
261
262 /*
263 * Adding actual entries
264 */
03690d13
TC
265 foreach ($this->entries as $entry) {
266 foreach ($this->tags as $tag) {
267 $pdf->SetKeywords($tag['value']);
268 }
269
270 $pdf->AddPage();
271 $html = '<h1>'.$entry->getTitle().'</h1>';
272 $html .= $entry->getContent();
add597ba 273
03690d13
TC
274 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
275 }
276
277 // set image scale factor
278 $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
279
add597ba
JB
280 return Response::create(
281 $pdf->Output('', 'S'),
282 200,
283 array(
284 'Content-Description' => 'File Transfer',
285 'Content-type' => 'application/pdf',
286 'Content-Disposition' => 'attachment; filename="'.$this->title.'.pdf"',
287 'Content-Transfer-Encoding' => 'binary',
288 )
289 )->send();
03690d13
TC
290 }
291
add597ba
JB
292 /**
293 * Inspired from CsvFileDumper.
294 */
03690d13
TC
295 private function produceCSV()
296 {
add597ba
JB
297 $delimiter = ';';
298 $enclosure = '"';
299 $handle = fopen('php://memory', 'rb+');
03690d13 300
add597ba 301 fputcsv($handle, array('Title', 'URL', 'Content', 'Tags', 'MIME Type', 'Language'), $delimiter, $enclosure);
03690d13 302
03690d13 303 foreach ($this->entries as $entry) {
add597ba
JB
304 fputcsv(
305 $handle,
306 array(
307 $entry->getTitle(),
308 $entry->getURL(),
cceca9ea
JB
309 // remove new line to avoid crazy results
310 str_replace(array("\r\n", "\r", "\n"), '', $entry->getContent()),
add597ba
JB
311 implode(', ', $entry->getTags()->toArray()),
312 $entry->getMimetype(),
313 $entry->getLanguage(),
314 ),
315 $delimiter,
316 $enclosure
317 );
318 }
319
320 rewind($handle);
321 $output = stream_get_contents($handle);
322 fclose($handle);
323
324 return Response::create(
325 $output,
326 200,
327 array(
328 'Content-type' => 'application/csv',
329 'Content-Disposition' => 'attachment; filename="'.$this->title.'.csv"',
330 'Content-Transfer-Encoding' => 'UTF-8',
331 )
332 )->send();
333 }
334
b3cc1a14
TC
335 private function produceJSON()
336 {
b3cc1a14 337 return Response::create(
8ac95cbf 338 $this->prepareSerializingContent('json'),
b3cc1a14
TC
339 200,
340 array(
341 'Content-type' => 'application/json',
342 'Content-Disposition' => 'attachment; filename="'.$this->title.'.json"',
343 'Content-Transfer-Encoding' => 'UTF-8',
344 )
345 )->send();
346 }
347
348 private function produceXML()
349 {
b3cc1a14 350 return Response::create(
8ac95cbf 351 $this->prepareSerializingContent('xml'),
b3cc1a14
TC
352 200,
353 array(
354 'Content-type' => 'application/xml',
355 'Content-Disposition' => 'attachment; filename="'.$this->title.'.xml"',
356 'Content-Transfer-Encoding' => 'UTF-8',
357 )
358 )->send();
359 }
8ac95cbf 360
b3cc1a14
TC
361 /**
362 * Return a Serializer object for producing processes that need it (JSON & XML).
363 *
364 * @return Serializer
365 */
8ac95cbf 366 private function prepareSerializingContent($format)
b3cc1a14 367 {
268e9e72 368 $serializer = SerializerBuilder::create()->build();
b3cc1a14 369
cceca9ea
JB
370 return $serializer->serialize(
371 $this->entries,
372 $format,
373 SerializationContext::create()->setGroups(array('entries_for_user'))
374 );
b3cc1a14
TC
375 }
376
add597ba
JB
377 /**
378 * Return a kind of footer / information for the epub.
379 *
380 * @param string $type Generator of the export, can be: tdpdf, PHPePub, PHPMobi
381 *
382 * @return string
383 */
384 private function getExportInformation($type)
385 {
386 $info = str_replace('%EXPORT_METHOD%', $type, $this->footerTemplate);
387
388 if ('tcpdf' === $type) {
389 return str_replace('%IMAGE%', '<img src="'.$this->logoPath.'" />', $info);
03690d13 390 }
add597ba
JB
391
392 return str_replace('%IMAGE%', '', $info);
03690d13
TC
393 }
394}