2015-10-15 18:06:59 +00:00
< ? php
namespace Wallabag\CoreBundle\Helper ;
2015-12-22 09:16:34 +00:00
use JMS\Serializer ;
use JMS\Serializer\SerializationContext ;
use JMS\Serializer\SerializerBuilder ;
2015-10-15 18:06:59 +00:00
use PHPePub\Core\EPub ;
use PHPePub\Core\Structure\OPF\DublinCore ;
2015-10-16 08:51:53 +00:00
use Symfony\Component\HttpFoundation\Response ;
2016-01-21 07:53:09 +00:00
use Craue\ConfigBundle\Util\Config ;
2015-10-15 18:06:59 +00:00
2015-10-30 19:57:10 +00:00
/**
* This class doesn 't have unit test BUT it' s fully covered by a functional test with ExportControllerTest .
*/
2015-10-15 18:06:59 +00:00
class EntriesExport
{
2015-10-16 08:51:53 +00:00
private $wallabagUrl ;
private $logoPath ;
private $title = '' ;
private $entries = array ();
2015-10-15 18:06:59 +00:00
private $authors = array ( 'wallabag' );
2015-10-16 08:51:53 +00:00
private $language = '' ;
private $tags = array ();
private $footerTemplate = ' < div style = " text-align:center; " >
< p > Produced by wallabag with % EXPORT_METHOD %</ 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 ' ;
2015-10-15 18:06:59 +00:00
2015-10-16 08:51:53 +00:00
/**
2016-01-21 07:53:09 +00:00
* @ param Config $craueConfig CraueConfig instance to get wallabag instance url from database
2015-10-16 08:51:53 +00:00
* @ param string $logoPath Path to the logo FROM THE BUNDLE SCOPE
*/
2016-01-21 07:53:09 +00:00
public function __construct ( Config $craueConfig , $logoPath )
2015-10-15 18:06:59 +00:00
{
2016-01-21 07:53:09 +00:00
$this -> wallabagUrl = $craueConfig -> get ( 'wallabag_url' );
2015-10-16 08:51:53 +00:00
$this -> logoPath = $logoPath ;
}
/**
* Define entries .
*
* @ param array | Entry $entries An array of entries or one entry
*/
public function setEntries ( $entries )
{
if ( ! is_array ( $entries )) {
$this -> language = $entries -> getLanguage ();
$entries = array ( $entries );
}
2015-10-15 18:06:59 +00:00
$this -> entries = $entries ;
foreach ( $entries as $entry ) {
$this -> tags [] = $entry -> getTags ();
}
2015-10-16 08:51:53 +00:00
return $this ;
2015-10-15 18:06:59 +00:00
}
/**
* Sets the category of which we want to get articles , or just one entry .
*
* @ param string $method Method to get articles
*/
2015-10-16 08:51:53 +00:00
public function updateTitle ( $method )
2015-10-15 18:06:59 +00:00
{
2015-10-16 08:51:53 +00:00
$this -> title = $method . ' articles' ;
if ( 'entry' === $method ) {
$this -> title = $this -> entries [ 0 ] -> getTitle ();
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
return $this ;
2015-10-15 18:06:59 +00:00
}
/**
* Sets the output format .
*
* @ param string $format
*/
public function exportAs ( $format )
{
2015-10-16 08:51:53 +00:00
switch ( $format ) {
2015-10-15 18:06:59 +00:00
case 'epub' :
2015-10-16 08:51:53 +00:00
return $this -> produceEpub ();
2015-10-15 18:06:59 +00:00
case 'mobi' :
2015-10-16 08:51:53 +00:00
return $this -> produceMobi ();
2015-10-15 18:06:59 +00:00
case 'pdf' :
2015-10-16 08:51:53 +00:00
return $this -> producePDF ();
2015-10-15 18:06:59 +00:00
case 'csv' :
2015-10-16 08:51:53 +00:00
return $this -> produceCSV ();
2015-10-18 13:49:00 +00:00
case 'json' :
return $this -> produceJSON ();
case 'xml' :
return $this -> produceXML ();
2016-01-25 16:31:45 +00:00
case 'txt' :
return $this -> produceTXT ();
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
throw new \InvalidArgumentException ( sprintf ( 'The format "%s" is not yet supported.' , $format ));
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
/**
* Use PHPePub to dump a . epub file .
*/
2015-10-15 18:06:59 +00:00
private function produceEpub ()
{
/*
* Start and End of the book
*/
$content_start =
" <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n "
. " <html xmlns= \" http://www.w3.org/1999/xhtml \" xmlns:epub= \" http://www.idpf.org/2007/ops \" > \n "
. '<head>'
. " <meta http-equiv= \" Default-Style \" content= \" text/html; charset=utf-8 \" /> \n "
2015-10-16 08:51:53 +00:00
. " <title>wallabag articles book</title> \n "
2015-10-15 18:06:59 +00:00
. " </head> \n "
. " <body> \n " ;
$bookEnd = " </body> \n </html> \n " ;
$book = new EPub ( EPub :: BOOK_VERSION_EPUB3 );
/*
* Book metadata
*/
$book -> setTitle ( $this -> title );
2015-10-16 08:51:53 +00:00
// Could also be the ISBN number, prefered for published books, or a UUID.
$book -> setIdentifier ( $this -> title , EPub :: IDENTIFIER_URI );
// 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.
$book -> setLanguage ( $this -> language );
$book -> setDescription ( 'Some articles saved on my wallabag' );
2015-10-15 18:06:59 +00:00
foreach ( $this -> authors as $author ) {
$book -> setAuthor ( $author , $author );
}
2015-10-16 08:51:53 +00:00
// I hope this is a non existant address :)
$book -> setPublisher ( 'wallabag' , 'wallabag' );
// Strictly not needed as the book date defaults to time().
$book -> setDate ( time ());
$book -> setSourceURL ( $this -> wallabagUrl );
2015-10-15 18:06:59 +00:00
$book -> addDublinCoreMetadata ( DublinCore :: CONTRIBUTOR , 'PHP' );
$book -> addDublinCoreMetadata ( DublinCore :: CONTRIBUTOR , 'wallabag' );
/*
* Front page
*/
2015-10-16 08:51:53 +00:00
if ( file_exists ( $this -> logoPath )) {
$book -> setCoverImage ( 'Cover.png' , file_get_contents ( $this -> logoPath ), 'image/png' );
}
2015-10-15 18:06:59 +00:00
2015-10-16 08:51:53 +00:00
$book -> addChapter ( 'Notices' , 'Cover2.html' , $content_start . $this -> getExportInformation ( 'PHPePub' ) . $bookEnd );
2015-10-15 18:06:59 +00:00
$book -> buildTOC ();
/*
* Adding actual entries
*/
2015-10-16 08:51:53 +00:00
// set tags as subjects
foreach ( $this -> entries as $entry ) {
foreach ( $this -> tags as $tag ) {
$book -> setSubject ( $tag [ 'value' ]);
}
2015-10-15 18:06:59 +00:00
$chapter = $content_start . $entry -> getContent () . $bookEnd ;
$book -> addChapter ( $entry -> getTitle (), htmlspecialchars ( $entry -> getTitle ()) . '.html' , $chapter , true , EPub :: EXTERNAL_REF_ADD );
}
2015-10-16 08:51:53 +00:00
return Response :: create (
$book -> getBook (),
200 ,
array (
'Content-Description' => 'File Transfer' ,
'Content-type' => 'application/epub+zip' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.epub"' ,
'Content-Transfer-Encoding' => 'binary' ,
)
) -> send ();
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
/**
* Use PHPMobi to dump a . mobi file .
*/
2015-10-15 18:06:59 +00:00
private function produceMobi ()
{
$mobi = new \MOBI ();
$content = new \MOBIFile ();
/*
* Book metadata
*/
$content -> set ( 'title' , $this -> title );
$content -> set ( 'author' , implode ( $this -> authors ));
$content -> set ( 'subject' , $this -> title );
/*
* Front page
*/
2015-10-16 08:51:53 +00:00
$content -> appendParagraph ( $this -> getExportInformation ( 'PHPMobi' ));
if ( file_exists ( $this -> logoPath )) {
$content -> appendImage ( imagecreatefrompng ( $this -> logoPath ));
}
2015-10-15 18:06:59 +00:00
$content -> appendPageBreak ();
/*
* Adding actual entries
*/
foreach ( $this -> entries as $entry ) {
$content -> appendChapterTitle ( $entry -> getTitle ());
$content -> appendParagraph ( $entry -> getContent ());
$content -> appendPageBreak ();
}
$mobi -> setContentProvider ( $content );
// the browser inside Kindle Devices doesn't likes special caracters either, we limit to A-z/0-9
$this -> title = preg_replace ( '/[^A-Za-z0-9\-]/' , '' , $this -> title );
2015-10-16 08:51:53 +00:00
return Response :: create (
$mobi -> toString (),
200 ,
array (
'Accept-Ranges' => 'bytes' ,
'Content-Description' => 'File Transfer' ,
'Content-type' => 'application/x-mobipocket-ebook' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.mobi"' ,
'Content-Transfer-Encoding' => 'binary' ,
)
) -> send ();
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
/**
* Use TCPDF to dump a . pdf file .
*/
2015-10-15 18:06:59 +00:00
private function producePDF ()
{
$pdf = new \TCPDF ( PDF_PAGE_ORIENTATION , PDF_UNIT , PDF_PAGE_FORMAT , true , 'UTF-8' , false );
/*
* Book metadata
*/
$pdf -> SetCreator ( PDF_CREATOR );
$pdf -> SetAuthor ( 'wallabag' );
$pdf -> SetTitle ( $this -> title );
$pdf -> SetSubject ( 'Articles via wallabag' );
$pdf -> SetKeywords ( 'wallabag' );
/*
* Front page
*/
$pdf -> AddPage ();
2015-10-16 08:51:53 +00:00
$intro = '<h1>' . $this -> title . '</h1>' . $this -> getExportInformation ( 'tcpdf' );
2015-10-15 18:06:59 +00:00
$pdf -> writeHTMLCell ( 0 , 0 , '' , '' , $intro , 0 , 1 , 0 , true , '' , true );
/*
* Adding actual entries
*/
foreach ( $this -> entries as $entry ) {
foreach ( $this -> tags as $tag ) {
$pdf -> SetKeywords ( $tag [ 'value' ]);
}
$pdf -> AddPage ();
$html = '<h1>' . $entry -> getTitle () . '</h1>' ;
$html .= $entry -> getContent ();
2015-10-16 08:51:53 +00:00
2015-10-15 18:06:59 +00:00
$pdf -> writeHTMLCell ( 0 , 0 , '' , '' , $html , 0 , 1 , 0 , true , '' , true );
}
// set image scale factor
$pdf -> setImageScale ( PDF_IMAGE_SCALE_RATIO );
2015-10-16 08:51:53 +00:00
return Response :: create (
$pdf -> Output ( '' , 'S' ),
200 ,
array (
'Content-Description' => 'File Transfer' ,
'Content-type' => 'application/pdf' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.pdf"' ,
'Content-Transfer-Encoding' => 'binary' ,
)
) -> send ();
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
/**
* Inspired from CsvFileDumper .
*/
2015-10-15 18:06:59 +00:00
private function produceCSV ()
{
2015-10-16 08:51:53 +00:00
$delimiter = ';' ;
$enclosure = '"' ;
$handle = fopen ( 'php://memory' , 'rb+' );
2015-10-15 18:06:59 +00:00
2015-10-16 08:51:53 +00:00
fputcsv ( $handle , array ( 'Title' , 'URL' , 'Content' , 'Tags' , 'MIME Type' , 'Language' ), $delimiter , $enclosure );
2015-10-15 18:06:59 +00:00
foreach ( $this -> entries as $entry ) {
2015-10-16 08:51:53 +00:00
fputcsv (
$handle ,
array (
$entry -> getTitle (),
$entry -> getURL (),
2015-10-30 19:57:10 +00:00
// remove new line to avoid crazy results
str_replace ( array ( " \r \n " , " \r " , " \n " ), '' , $entry -> getContent ()),
2015-10-16 08:51:53 +00:00
implode ( ', ' , $entry -> getTags () -> toArray ()),
$entry -> getMimetype (),
$entry -> getLanguage (),
),
$delimiter ,
$enclosure
);
}
rewind ( $handle );
$output = stream_get_contents ( $handle );
fclose ( $handle );
return Response :: create (
$output ,
200 ,
array (
'Content-type' => 'application/csv' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.csv"' ,
'Content-Transfer-Encoding' => 'UTF-8' ,
)
) -> send ();
}
2015-10-18 13:49:00 +00:00
private function produceJSON ()
{
return Response :: create (
2015-10-18 13:59:15 +00:00
$this -> prepareSerializingContent ( 'json' ),
2015-10-18 13:49:00 +00:00
200 ,
array (
'Content-type' => 'application/json' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.json"' ,
'Content-Transfer-Encoding' => 'UTF-8' ,
)
) -> send ();
}
private function produceXML ()
{
return Response :: create (
2015-10-18 13:59:15 +00:00
$this -> prepareSerializingContent ( 'xml' ),
2015-10-18 13:49:00 +00:00
200 ,
array (
'Content-type' => 'application/xml' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.xml"' ,
'Content-Transfer-Encoding' => 'UTF-8' ,
)
) -> send ();
}
2015-10-18 13:59:15 +00:00
2016-01-25 16:31:45 +00:00
private function produceTXT ()
{
$content = '' ;
2016-02-01 14:35:30 +00:00
$bar = str_repeat ( '=' , 100 );
2016-01-25 16:31:45 +00:00
foreach ( $this -> entries as $entry ) {
2016-02-01 14:35:30 +00:00
$content .= " \n \n " . $bar . " \n \n " . $entry -> getTitle () . " \n \n " . $bar . " \n \n " ;
$content .= trim ( preg_replace ( '/\s+/S' , ' ' , strip_tags ( $entry -> getContent ()))) . " \n \n " ;
2016-01-25 16:31:45 +00:00
}
2016-02-01 14:35:30 +00:00
2016-01-25 16:31:45 +00:00
return Response :: create (
$content ,
200 ,
array (
'Content-type' => 'text/plain' ,
'Content-Disposition' => 'attachment; filename="' . $this -> title . '.txt"' ,
'Content-Transfer-Encoding' => 'UTF-8' ,
)
) -> send ();
}
2015-10-18 13:49:00 +00:00
/**
* Return a Serializer object for producing processes that need it ( JSON & XML ) .
*
* @ return Serializer
*/
2015-10-18 13:59:15 +00:00
private function prepareSerializingContent ( $format )
2015-10-18 13:49:00 +00:00
{
2015-10-19 19:17:30 +00:00
$serializer = SerializerBuilder :: create () -> build ();
2015-10-18 13:49:00 +00:00
2015-10-30 19:57:10 +00:00
return $serializer -> serialize (
$this -> entries ,
$format ,
SerializationContext :: create () -> setGroups ( array ( 'entries_for_user' ))
);
2015-10-18 13:49:00 +00:00
}
2015-10-16 08:51:53 +00:00
/**
* Return a kind of footer / information for the epub .
*
* @ param string $type Generator of the export , can be : tdpdf , PHPePub , PHPMobi
*
* @ return string
*/
private function getExportInformation ( $type )
{
$info = str_replace ( '%EXPORT_METHOD%' , $type , $this -> footerTemplate );
if ( 'tcpdf' === $type ) {
return str_replace ( '%IMAGE%' , '<img src="' . $this -> logoPath . '" />' , $info );
2015-10-15 18:06:59 +00:00
}
2015-10-16 08:51:53 +00:00
return str_replace ( '%IMAGE%' , '' , $info );
2015-10-15 18:06:59 +00:00
}
}