1<?php
2/**
3 * DokuWiki Plugin ebookexport (Action Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Nomen Nescio <info@nomennesc.io>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class action_plugin_ebookexport extends DokuWiki_Action_Plugin {
13
14    /**
15     * Registers a callback function for a given event
16     *
17     * @param Doku_Event_Handler $controller DokuWiki's event controller object
18     * @return void
19     */
20    public function register(Doku_Event_Handler $controller) {
21       $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'exportepub', array());
22       $controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'addepubbutton', array());
23    }
24
25    public function exportepub(Doku_Event $event) {
26        global $ACT;
27        global $ID;
28        global $conf;
29
30        // our event?
31        if($ACT != 'export_epub') return false;
32
33        // check user's rights
34        if(auth_quickaclcheck($ID) < AUTH_READ) return false;
35
36        // it's ours, no one else's
37        $event->preventDefault();
38
39	$tempdir=tempnam(sys_get_temp_dir(),'');
40	if (file_exists($tempdir)) { unlink($tempdir); }
41	mkdir($tempdir);
42
43        require_once DOKU_INC . 'inc/parser/parser.php';
44
45	$mypage = pageinfo();
46	$mypath = $mypage["filepath"];
47	$doc = file_get_contents($mypath);
48
49	$instructions = p_get_instructions($doc);
50	require_once DOKU_INC . 'inc/parser/xhtml.php';
51	$Renderer = new Doku_Renderer_XHTML();
52	foreach ( $instructions as $instruction ) {
53	    call_user_func_array(array(&$Renderer, $instruction[0]),$instruction[1]);
54	}
55
56	$tocarray = $Renderer->toc;
57	$pages = $Renderer->doc;
58
59	if(isset($conf['baseurl']) && ($conf['baseurl'] != '')){
60	    $url = $conf['baseurl'] . '/';
61	}
62	else{
63            $self = parse_url(DOKU_URL);
64            $url = $self['scheme'] . '://' . $self['host'];
65            if($self['port']) {
66                $url .= ':' . $self['port'];
67            }
68	    $url .= '/';
69	}
70	$pages = preg_replace('/href="\//',"href=\"$url",$pages);
71
72	file_put_contents($tempdir . "/pages.xhtml",$pages);
73
74	$mimetype = "application/epub+zip";
75        file_put_contents($tempdir . "/mimetype",$mimetype);
76
77	$pagestyle = "@page {\nmargin-bottom: 5pt;\nmargin-top: 5pt;\n}\n";
78        file_put_contents($tempdir . "/page_styles.css",$pagestyle);
79
80	$stylesheet = "h2 {\nfont-size: large;\n}\n";
81        file_put_contents($tempdir . "/stylesheet.css",$stylesheet);
82
83	mkdir($tempdir . "/META-INF");
84
85	$container = '<?xml version="1.0"?><container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"><rootfiles><rootfile full-path="content.opf" media-type="application/oebps-package+xml"/></rootfiles></container>';
86	file_put_contents($tempdir . "/META-INF/container.xml", $container);
87
88	if(isset($mypage["meta"]["title"])){
89	    $mytitle = $mypage["meta"]["title"];
90	} else{
91	    $mytitle = preg_replace('/^.*:/','',$ID);
92	    $mytitle = ucwords(preg_replace('/_/',' ',$mytitle));
93	}
94
95	$epubuuid = preg_replace('/^(........)(....)(....)(....)(............).*/','${1}-${2}-${3}-${4}-${5}',md5($conf['title'] . $mytitle . $mypage['lastmod']));
96	$files = array('content.opf','mimetype','page_styles.css','pages.xhtml','stylesheet.css','titlepage.xhtml','toc.ncx','META-INF/container.xml');
97
98	$content = "<?xml version='1.0' encoding='utf-8'?>";
99	$content .= '<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="uuid_id">';
100	$content .= '<metadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata" xmlns:dc="http://purl.org/dc/elements/1.1/">';
101	$content .= '<dc:language>en</dc:language>';
102	$content .= '<dc:title>' . $mytitle . '</dc:title>';
103	$content .= '<dc:creator opf:file-as="' . $conf['title'] . '" opf:role="aut">' . $conf['title'] . '</dc:creator>';
104	$content .= '<meta name="cover" content="cover"/>';
105	$content .= '<dc:date>' . date("Y-m-d\TH:i:s:P",$mypage['lastmod']) . '</dc:date>';
106	$content .= '<dc:contributor opf:role="bkp"></dc:contributor>';
107	$content .= '<dc:identifier id="uuid_id" opf:scheme="uuid">' . $epubuuid . '</dc:identifier>';
108	$content .= '</metadata>';
109	$content .= '<manifest>';
110	if(isset($mypage["meta"]["relation"]["media"])){
111            foreach($mypage["meta"]["relation"]["media"] as $mediaitem => $whatever){
112            	$mediafile = mediaFN($mediaitem);
113		$mediamime = mime_content_type($mediafile);
114                $medialink = ml($mediaitem);
115                $mediadir = dirname($medialink);
116		if(!is_dir($tempdir . $mediadir))
117		    mkdir($tempdir . $mediadir, 0777, true);
118		copy($mediafile,$tempdir . $medialink);
119		array_push($files,substr($medialink,1));
120		$content .= '<item href="' . $medialink . '" id="' . $mediaitem . '" media-type="' . $mediamime . '"/>';
121            }
122	}
123	$content .= '<item href="pages.xhtml" id="id1" media-type="application/xhtml+xml"/>';
124	$content .= '<item href="page_styles.css" id="page_css" media-type="text/css"/>';
125	$content .= '<item href="stylesheet.css" id="css" media-type="text/css"/>';
126	$content .= '<item href="titlepage.xhtml" id="titlepage" media-type="application/xhtml+xml"/>';
127	$content .= '<item href="toc.ncx" media-type="application/x-dtbncx+xml" id="ncx"/>';
128	$content .= '</manifest>';
129	$content .= '<spine toc="ncx">';
130	$content .= '<itemref idref="titlepage"/>';
131	$content .= '<itemref idref="id1"/>';
132	$content .= '</spine>';
133	$content .= '<guide>';
134	$content .= '<reference href="titlepage.xhtml" type="cover" title="Cover"/>';
135	$content .= '</guide></package>';
136        file_put_contents($tempdir . "/content.opf",$content);
137
138	$titlepage = "<?xml version='1.0' encoding='utf-8'?>";
139	$titlepage .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">';
140	$titlepage .= '<head>';
141	$titlepage .= '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>';
142	$titlepage .= '<title>Cover</title>';
143	$titlepage .= '<style type="text/css" title="override_css">';
144	$titlepage .= '@page {padding: 0pt; margin:0pt}';
145	$titlepage .= 'body { text-align: center; padding:0pt; margin: 0pt; }';
146	$titlepage .= '</style>';
147	$titlepage .= '</head>';
148	$titlepage .= '<body>';
149	$titlepage .= '<div>';
150	$titlepage .= '<h1>' . $mytitle . '</h1>';
151	$titlepage .= '<h2>' . $conf['title'] . '</h2>';
152	$titlepage .= '</div>';
153	$titlepage .= '</body></html>';
154        file_put_contents($tempdir . "/titlepage.xhtml",$titlepage);
155
156	$toc = "<?xml version='1.0' encoding='utf-8'?>";
157	$toc .= '<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="eng">';
158	$toc .= '<head>';
159	$toc .= '<meta content="0c159d12-f5fe-4323-8194-f5c652b89f5c" name="dtb:uid"/>';
160	$toc .= '<meta content="2" name="dtb:depth"/>';
161	$toc .= '<meta content="calibre (0.8.68)" name="dtb:generator"/>';
162	$toc .= '<meta content="0" name="dtb:totalPageCount"/>';
163	$toc .= '<meta content="0" name="dtb:maxPageNumber"/>';
164	$toc .= '</head>';
165	$toc .= '<docTitle>';
166	$toc .= '<text>Titel</text>';
167	$toc .= '</docTitle>';
168	$toc .= '<navMap>';
169	for($i=0;$i<sizeof($tocarray);$i++){
170	    $toc .= '<navPoint id="' . substr($tocarray[$i]["link"],1) . '" playOrder="' . $i . '">';
171	    $toc .= '<navLabel><text>' . $tocarray[$i]["title"] . '</text>';
172	    $toc .= '</navLabel><content src="pages.xhtml' . $tocarray[$i]["link"] . '"/>';
173	    $toc .= '</navPoint>';
174	}
175	$toc .= '</navMap></ncx>';
176        file_put_contents($tempdir . "/toc.ncx",$toc);
177
178	$zip = new ZipArchive();
179	$zipfile = $tempdir . "/" . $ID . ".epub";
180	$zip->open($zipfile,ZipArchive::CREATE);
181	foreach($files as $file){
182		$zip->addFile($tempdir . "/" . $file,$file);
183	}
184	$zip->close();
185
186        header('Content-Type: application/epub+zip');
187        header('Cache-Control: must-revalidate, no-cache');
188        header('Pragma: no-cache');
189
190        $filename = preg_replace('/ /','_',$mytitle) . ".epub";
191        header('Content-Disposition: attachment; filename="' . $filename . '";');
192
193        //Bookcreator uses jQuery.fileDownload.js, which requires a cookie.
194        header('Set-Cookie: fileDownload=true; path=/');
195
196        $fp = @fopen($zipfile, "rb");
197        if($fp) {
198	    $size = filesize($zipfile);
199	    header('Accept-Ranges: bytes');
200            header("Content-Length: $size");
201	    fseek($fp,0);
202	    $chunk = ($size > HTTP_CHUNK_SIZE) ? HTTP_CHUNK_SIZE : $size;
203	    while (!feof($fp) && $chunk > 0) {
204		@set_time_limit(30);
205		print fread($fp, $chunk);
206		flush();
207		$size -= $chunk;
208		$chunk = ($size > HTTP_CHUNK_SIZE) ? HTTP_CHUNK_SIZE : $size;
209	    }
210        } else {
211            header("HTTP/1.0 500 Internal Server Error");
212            print "Could not read file - bad permissions?";
213            $ranges[] = array(0,$size,$size);
214        }
215	$this->rrmdir($tempdir);
216	exit;
217    }
218
219    private function rrmdir($dir) {
220        if (is_dir($dir)) {
221            $objects = scandir($dir);
222            foreach ($objects as $object) {
223                if ($object != "." && $object != "..") {
224                    if (is_dir($dir."/".$object))
225                        $this->rrmdir($dir."/".$object);
226                    else
227                        unlink($dir."/".$object);
228                }
229            }
230            rmdir($dir);
231        }
232    }
233
234    public function addepubbutton(Doku_Event $event) {
235        global $ID, $REV;
236
237        if($event->data['view'] == 'main') {
238            $params = array('do' => 'export_epub');
239            if($REV) {
240                $params['rev'] = $REV;
241            }
242
243            // insert button at position before last (up to top)
244            $event->data['items'] = array_slice($event->data['items'], 0, -1, true) +
245                array('export_epub' =>
246                          '<li>'
247                          . '<a href="' . wl($ID, $params) . '"  class="action export_epub" rel="nofollow" title="Export to EPUB">'
248                          . '<span>Export to EPUB</span>'
249                          . '</a>'
250                          . '</li>'
251                ) +
252                array_slice($event->data['items'], -1, 1, true);
253        }
254    }
255}
256
257// vim:ts=4:sw=4:et:
258