533 lines
17 KiB
PHP
533 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* ODT export Plugin component. Mainly based at dw2pdf export action plugin component.
|
|
*
|
|
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
|
* @author Luigi Micco <l.micco@tiscali.it>
|
|
* @author Andreas Gohr <andi@splitbrain.org>
|
|
* @author Gerrit Uitslag <klapinklapin@gmail.com>
|
|
*/
|
|
|
|
// must be run within Dokuwiki
|
|
if(!defined('DOKU_INC')) die();
|
|
|
|
use dokuwiki\Action\Exception\ActionException;
|
|
use dokuwiki\Action\Exception\ActionAbort;
|
|
|
|
/**
|
|
* Class action_plugin_odt_export
|
|
*
|
|
* Collect pages and export these. GUI is available via bookcreator.
|
|
*
|
|
* @package DokuWiki\Action\Export
|
|
*/
|
|
class action_plugin_odt_export extends DokuWiki_Action_Plugin {
|
|
protected $config = null;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $list = array();
|
|
|
|
/**
|
|
* Register the events
|
|
*
|
|
* @param Doku_Event_Handler $controller
|
|
*/
|
|
public function register(Doku_Event_Handler $controller) {
|
|
$controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'convert', array());
|
|
$controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'addbutton_odt', array());
|
|
$controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'addbutton_pdf', array());
|
|
$controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'addbutton_odt_new', array());
|
|
$controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'addbutton_pdf_new', array());
|
|
}
|
|
|
|
/**
|
|
* Add 'export odt'-button to pagetools
|
|
*
|
|
* @param Doku_Event $event
|
|
*/
|
|
public function addbutton_odt(Doku_Event $event) {
|
|
global $ID, $REV;
|
|
|
|
if($this->getConf('showexportbutton') && $event->data['view'] == 'main') {
|
|
$params = array('do' => 'export_odt');
|
|
if($REV) {
|
|
$params['rev'] = $REV;
|
|
}
|
|
|
|
// insert button at position before last (up to top)
|
|
$event->data['items'] = array_slice($event->data['items'], 0, -1, true) +
|
|
array('export_odt' =>
|
|
'<li>'
|
|
. '<a href="' . wl($ID, $params) . '" class="action export_odt" rel="nofollow" title="' . $this->getLang('export_odt_button') . '">'
|
|
. '<span>' . $this->getLang('export_odt_button') . '</span>'
|
|
. '</a>'
|
|
. '</li>'
|
|
) +
|
|
array_slice($event->data['items'], -1, 1, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add 'export odt=>pdf'-button to pagetools
|
|
*
|
|
* @param Doku_Event $event
|
|
*/
|
|
public function addbutton_pdf(Doku_Event $event) {
|
|
global $ID, $REV;
|
|
|
|
if($this->getConf('showpdfexportbutton') && $event->data['view'] == 'main') {
|
|
$params = array('do' => 'export_odt_pdf');
|
|
if($REV) {
|
|
$params['rev'] = $REV;
|
|
}
|
|
|
|
// insert button at position before last (up to top)
|
|
$event->data['items'] = array_slice($event->data['items'], 0, -1, true) +
|
|
array('export_odt_pdf' =>
|
|
'<li>'
|
|
. '<a href="' . wl($ID, $params) . '" class="action export_odt_pdf" rel="nofollow" title="' . $this->getLang('export_odt_pdf_button') . '">'
|
|
. '<span>' . $this->getLang('export_odt_pdf_button') . '</span>'
|
|
. '</a>'
|
|
. '</li>'
|
|
) +
|
|
array_slice($event->data['items'], -1, 1, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add 'export odt' button to page tools, new SVG based mechanism
|
|
*
|
|
* @param Doku_Event $event
|
|
*/
|
|
public function addbutton_odt_new(Doku_Event $event) {
|
|
if($event->data['view'] != 'page') return;
|
|
if($this->getConf('showexportbutton')) {
|
|
array_splice($event->data['items'], -1, 0, [new \dokuwiki\plugin\odt\MenuItemODT()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add 'export odt pdf' button to page tools, new SVG based mechanism
|
|
*
|
|
* @param Doku_Event $event
|
|
*/
|
|
public function addbutton_pdf_new(Doku_Event $event) {
|
|
if($event->data['view'] != 'page') return;
|
|
if($this->getConf('showpdfexportbutton')) {
|
|
array_splice($event->data['items'], -1, 0, [new \dokuwiki\plugin\odt\MenuItemODTPDF()]);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************************
|
|
* Book export *
|
|
***********************************************************************************/
|
|
|
|
/**
|
|
* Do article(s) to ODT conversion work
|
|
*
|
|
* @param Doku_Event $event
|
|
* @return bool
|
|
*/
|
|
public function convert(Doku_Event $event) {
|
|
global $ID;
|
|
$format = NULL;
|
|
|
|
$action = act_clean($event->data);
|
|
|
|
// Any kind of ODT export?
|
|
$odt_export = false;
|
|
if (strncmp($action, 'export_odt', strlen('export_odt')) == 0) {
|
|
$odt_export = true;
|
|
}
|
|
|
|
// check conversion format
|
|
if ($odt_export && strpos($action, '_pdf') !== false) {
|
|
$format = 'pdf';
|
|
}
|
|
|
|
// single page export:
|
|
// rename action to the actual renderer component
|
|
if($action == 'export_odt') {
|
|
$event->data = 'export_odt_page';
|
|
} else if ($action == 'export_odt_pdf') {
|
|
$event->data = 'export_odt_pagepdf';
|
|
}
|
|
|
|
if( !is_array($action) && $odt_export ) {
|
|
// On export to ODT load config helper if not done yet
|
|
// and stop on errors.
|
|
if ( $this->config == NULL ) {
|
|
$this->config = plugin_load('helper', 'odt_config');
|
|
$this->config->load($warning);
|
|
|
|
if (!empty($warning)) {
|
|
$this->showPageWithErrorMsg($event, NULL, $warning);
|
|
return false;
|
|
}
|
|
}
|
|
$this->config->setConvertTo($format);
|
|
}
|
|
|
|
// the book export?
|
|
if(($action != 'export_odtbook') && ($action != 'export_odtns')) return false;
|
|
|
|
// check user's rights
|
|
if(auth_quickaclcheck($ID) < AUTH_READ) return false;
|
|
|
|
if($data = $this->collectExportPages($event)) {
|
|
list($title, $this->list) = $data;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
// it's ours, no one else's
|
|
$event->preventDefault();
|
|
|
|
// prepare cache and its dependencies
|
|
$depends = array();
|
|
$cache = $this->prepareCache($title, $depends);
|
|
|
|
// hard work only when no cache available
|
|
if(!$this->getConf('usecache') || !$cache->useCache($depends)) {
|
|
$this->generateODT($cache->cache, $title);
|
|
}
|
|
|
|
// deliver the file
|
|
$this->sendODTFile($cache->cache, $title);
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Obtain list of pages and title, based on url parameters
|
|
*
|
|
* @param Doku_Event $event
|
|
* @return string|bool
|
|
*/
|
|
protected function collectExportPages(Doku_Event $event) {
|
|
global $ID;
|
|
global $INPUT;
|
|
|
|
// Load config helper if not done yet
|
|
if ( $this->config == NULL ) {
|
|
$this->config = plugin_load('helper', 'odt_config');
|
|
$this->config->load($warning);
|
|
}
|
|
|
|
// list of one or multiple pages
|
|
$list = array();
|
|
|
|
$action = $event->data;
|
|
if($action == 'export_odt') {
|
|
$list[0] = $ID;
|
|
$title = $INPUT->str('book_title');
|
|
if(!$title) {
|
|
$title = p_get_first_heading($ID);
|
|
}
|
|
|
|
} elseif($action == 'export_odtns') {
|
|
//check input for title and ns
|
|
if(!$title = $INPUT->str('book_title')) {
|
|
$this->showPageWithErrorMsg($event, 'needtitle');
|
|
return false;
|
|
}
|
|
$docnamespace = cleanID($INPUT->str('book_ns'));
|
|
if(!@is_dir(dirname(wikiFN($docnamespace . ':dummy')))) {
|
|
$this->showPageWithErrorMsg($event, 'needns');
|
|
return false;
|
|
}
|
|
|
|
//sort order
|
|
$order = $INPUT->str('book_order', 'natural', true);
|
|
$sortoptions = array('pagename', 'date', 'natural');
|
|
if(!in_array($order, $sortoptions)) {
|
|
$order = 'natural';
|
|
}
|
|
|
|
//search depth
|
|
$depth = $INPUT->int('book_nsdepth', 0);
|
|
if($depth < 0) {
|
|
$depth = 0;
|
|
}
|
|
|
|
//page search
|
|
$result = array();
|
|
$opts = array('depth' => $depth); //recursive all levels
|
|
$dir = utf8_encodeFN(str_replace(':', '/', $docnamespace));
|
|
search($result, $this->config->getParam('datadir'), 'search_allpages', $opts, $dir);
|
|
|
|
//sorting
|
|
if(count($result) > 0) {
|
|
if($order == 'date') {
|
|
usort($result, array($this, '_datesort'));
|
|
} elseif($order == 'pagename') {
|
|
usort($result, array($this, '_pagenamesort'));
|
|
}
|
|
}
|
|
|
|
foreach($result as $item) {
|
|
$list[] = $item['id'];
|
|
}
|
|
|
|
} elseif(isset($_COOKIE['list-pagelist']) && !empty($_COOKIE['list-pagelist'])) {
|
|
// Here is $action == 'export_odtbook'
|
|
|
|
/** @deprecated April 2016 replaced by localStorage version of Bookcreator*/
|
|
|
|
//is in Bookmanager of bookcreator plugin a title given?
|
|
if(!$title = $INPUT->str('book_title')) {
|
|
$this->showPageWithErrorMsg($event, 'needtitle');
|
|
return false;
|
|
} else {
|
|
$list = explode("|", $_COOKIE['list-pagelist']);
|
|
}
|
|
|
|
} elseif($INPUT->has('selection')) {
|
|
//handle Bookcreator requests based at localStorage
|
|
// if(!checkSecurityToken()) {
|
|
// http_status(403);
|
|
// print $this->getLang('empty');
|
|
// exit();
|
|
// }
|
|
|
|
$json = new JSON(JSON_LOOSE_TYPE);
|
|
$list = $json->decode($INPUT->post->str('selection', '', true));
|
|
if(!is_array($list) || empty($list)) {
|
|
http_status(400);
|
|
print $this->getLang('empty');
|
|
exit();
|
|
}
|
|
|
|
$title = $INPUT->str('pdfbook_title'); //DEPRECATED
|
|
$title = $INPUT->str('book_title', $title, true);
|
|
if(empty($title)) {
|
|
http_status(400);
|
|
print $this->getLang('needtitle');
|
|
exit();
|
|
}
|
|
|
|
} else {
|
|
//show empty bookcreator message
|
|
$this->showPageWithErrorMsg($event, 'empty');
|
|
return false;
|
|
}
|
|
|
|
$list = array_map('cleanID', $list);
|
|
|
|
$skippedpages = array();
|
|
foreach($list as $index => $pageid) {
|
|
if(auth_quickaclcheck($pageid) < AUTH_READ) {
|
|
$skippedpages[] = $pageid;
|
|
unset($list[$index]);
|
|
}
|
|
}
|
|
$list = array_filter($list); //removes also pages mentioned '0'
|
|
|
|
//if selection contains forbidden pages throw (overridable) warning
|
|
if(!$INPUT->bool('book_skipforbiddenpages') && !empty($skippedpages)) {
|
|
$msg = sprintf($this->getLang('forbidden'), hsc(join(', ', $skippedpages)));
|
|
if($INPUT->has('selection')) {
|
|
http_status(400);
|
|
print $msg;
|
|
exit();
|
|
} else {
|
|
$this->showPageWithErrorMsg($event, null, $msg);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return array($title, $list);
|
|
}
|
|
|
|
|
|
/**
|
|
* Set error notification and reload page again
|
|
*
|
|
* @param Doku_Event $event
|
|
* @param string $msglangkey key of translation key
|
|
*/
|
|
private function showPageWithErrorMsg(Doku_Event $event, $msglangkey, $translatedMsg=NULL) {
|
|
if (!empty($msglangkey)) {
|
|
// Message need to be translated.
|
|
msg($this->getLang($msglangkey), -1);
|
|
} else {
|
|
// Message already has been translated.
|
|
msg($translatedMsg, -1);
|
|
}
|
|
|
|
$event->data = 'show';
|
|
$_SERVER['REQUEST_METHOD'] = 'POST'; //clears url
|
|
}
|
|
|
|
/**
|
|
* Prepare cache
|
|
*
|
|
* @param string $title
|
|
* @param array $depends (reference) array with dependencies
|
|
* @return cache
|
|
*/
|
|
protected function prepareCache($title, &$depends) {
|
|
global $REV;
|
|
global $INPUT;
|
|
|
|
//different caches for varying config settings
|
|
$template = $this->getConf("odt_template");
|
|
$template = $INPUT->get->str('odt_template', $template, true);
|
|
|
|
|
|
$cachekey = join(',', $this->list)
|
|
. $REV
|
|
. $template
|
|
. $title;
|
|
$cache = new cache($cachekey, '.odt');
|
|
|
|
$dependencies = array();
|
|
foreach($this->list as $pageid) {
|
|
$relations = p_get_metadata($pageid, 'relation');
|
|
|
|
if(is_array($relations)) {
|
|
if(array_key_exists('media', $relations) && is_array($relations['media'])) {
|
|
foreach($relations['media'] as $mediaid => $exists) {
|
|
if($exists) {
|
|
$dependencies[] = mediaFN($mediaid);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(array_key_exists('haspart', $relations) && is_array($relations['haspart'])) {
|
|
foreach($relations['haspart'] as $part_pageid => $exists) {
|
|
if($exists) {
|
|
$dependencies[] = wikiFN($part_pageid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$dependencies[] = metaFN($pageid, '.meta');
|
|
}
|
|
|
|
$depends['files'] = array_map('wikiFN', $this->list);
|
|
$depends['files'][] = __FILE__;
|
|
$depends['files'][] = dirname(__FILE__) . '/../renderer/page.php';
|
|
$depends['files'][] = dirname(__FILE__) . '/../renderer/book.php';
|
|
$depends['files'][] = dirname(__FILE__) . '/../plugin.info.txt';
|
|
$depends['files'] = array_merge(
|
|
$depends['files'],
|
|
$dependencies,
|
|
getConfigFiles('main')
|
|
);
|
|
return $cache;
|
|
}
|
|
|
|
/**
|
|
* Build a ODT from the articles
|
|
*
|
|
* @param string $cachefile
|
|
* @param string $title
|
|
*/
|
|
protected function generateODT($cachefile, $title) {
|
|
global $ID;
|
|
global $REV;
|
|
|
|
/** @var renderer_plugin_odt_book $odt */
|
|
$odt = plugin_load('renderer','odt_book');
|
|
|
|
// store original pageid
|
|
$keep = $ID;
|
|
|
|
// loop over all pages
|
|
$xmlcontent = '';
|
|
foreach($this->list as $page) {
|
|
$filename = wikiFN($page, $REV);
|
|
|
|
if(!file_exists($filename)) {
|
|
continue;
|
|
}
|
|
// set global pageid to the rendered page
|
|
$ID = $page;
|
|
$xmlcontent .= p_render('odt_book', p_cached_instructions($filename, false, $page), $info);
|
|
}
|
|
|
|
//restore ID
|
|
$ID = $keep;
|
|
|
|
$odt->doc = $xmlcontent;
|
|
$odt->setTitle($title);
|
|
$odt->finalize_ODTfile();
|
|
|
|
// write to cache file
|
|
io_savefile($cachefile, $odt->doc);
|
|
}
|
|
|
|
/**
|
|
* @param string $cachefile
|
|
* @param string $title
|
|
*/
|
|
protected function sendODTFile($cachefile, $title) {
|
|
header('Content-Type: application/vnd.oasis.opendocument.text');
|
|
header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0');
|
|
header('Pragma: public');
|
|
http_conditionalRequest(filemtime($cachefile));
|
|
|
|
$filename = rawurlencode(cleanID(strtr($title, ':/;"', ' ')));
|
|
if($this->getConf('output') == 'file') {
|
|
header('Content-Disposition: attachment; filename="' . $filename . '.odt";');
|
|
} else {
|
|
header('Content-Disposition: inline; filename="' . $filename . '.odt";');
|
|
}
|
|
|
|
//Bookcreator uses jQuery.fileDownload.js, which requires a cookie.
|
|
header('Set-Cookie: fileDownload=true; path=/');
|
|
|
|
//try to send file, and exit if done
|
|
http_sendfile($cachefile);
|
|
|
|
$fp = @fopen($cachefile, "rb");
|
|
if($fp) {
|
|
http_rangeRequest($fp, filesize($cachefile), 'application/vnd.oasis.opendocument.text');
|
|
} else {
|
|
header("HTTP/1.0 500 Internal Server Error");
|
|
print "Could not read file - bad permissions?";
|
|
}
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Returns array of wiki pages which will be included in the exported document
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getExportedPages() {
|
|
return $this->list;
|
|
}
|
|
|
|
/**
|
|
* usort callback to sort by file lastmodified time
|
|
*
|
|
* @param array $a
|
|
* @param array $b
|
|
* @return int
|
|
*/
|
|
public function _datesort($a, $b) {
|
|
if($b['rev'] < $a['rev']) return -1;
|
|
if($b['rev'] > $a['rev']) return 1;
|
|
return strcmp($b['id'], $a['id']);
|
|
}
|
|
|
|
/**
|
|
* usort callback to sort by page id
|
|
*
|
|
* @param array $a
|
|
* @param array $b
|
|
* @return int
|
|
*/
|
|
public function _pagenamesort($a, $b) {
|
|
if($a['id'] <= $b['id']) return -1;
|
|
if($a['id'] > $b['id']) return 1;
|
|
return 0;
|
|
}
|
|
}
|