From c1de232b4c50e8fa29918c1b147e324649632bcc Mon Sep 17 00:00:00 2001 From: Roland Wunderling Date: Tue, 29 Sep 2015 09:21:06 +0200 Subject: [PATCH 1/3] Add support for downloading CSV file Extended plugin to provide this, by adding the options: import= export=:dokuwiki:media:path:where:to:write:mediafile.csv linkname=Name of the download link to create Thus, I changed the syntax to use '|' as delimiter between options rather than using ' '. I feel this is "more dokuwiki" :). However, this breaks existing dokuwiki pages that use the csv plugin to display a CVS file, which now would have to replace the separating space (' ') with '|import='. The idea behind this extension is that I want to maintain a list (in my case a member list) in dokuwiki but provide a download link to the csv file (also for users with only read access to the page). This is now supported as shown in the following example: Name,Email , Paul,paul@email.com Andreads,andreas@dokuwiki.org It renders the CSV data as a DokuWiki table but adds a CSV download link with the title "Download member list". This is done in the following way: When rendering the CSV data, this data is also copied to the export= file. Finally, a link to the just generated file is provided. --- syntax.php | 51 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/syntax.php b/syntax.php index f47ab6e..2b4c9b5 100644 --- a/syntax.php +++ b/syntax.php @@ -59,7 +59,9 @@ function handle($match, $state, $pos, Doku_Handler &$handler) { 'hdr_rows' => 1, 'hdr_cols' => 0, 'span_empty_cols' => 0, - 'file' => '', + 'import' => '', + 'export' => '', + 'linkname' => 'Download CSV file', 'delim' => ',', 'enclosure' => '"', 'escape' => '"', @@ -70,18 +72,18 @@ function handle($match, $state, $pos, Doku_Handler &$handler) { unset($match); // parse options - $optsin = explode(' ', $optstr); + $optsin = explode('|', $optstr); foreach($optsin as $o) { $o = trim($o); if(preg_match('/(\w+)=(.*)/', $o, $matches)) { $opt[$matches[1]] = $matches[2]; } elseif($o) { if(preg_match('/^https?:\/\//i', $o)) { - $opt['file'] = $o; + $opt['import'] = $o; } else { - $opt['file'] = cleanID($o); - if(!strlen(getNS($opt['file']))) - $opt['file'] = $INFO['namespace'].':'.$opt['file']; + $opt['import'] = cleanID($o); + if(!strlen(getNS($opt['import']))) + $opt['import'] = $INFO['namespace'].':'.$opt['import']; } } } @@ -97,22 +99,22 @@ function render($mode, Doku_Renderer &$renderer, $opt) { if($mode == 'metadata') return false; // load file data - if($opt['file']) { - if(preg_match('/^https?:\/\//i', $opt['file'])) { + if($opt['import']) { + if(preg_match('/^https?:\/\//i', $opt['import'])) { require_once(DOKU_INC.'inc/HTTPClient.php'); $http = new DokuHTTPClient(); - $opt['content'] = $http->get($opt['file']); + $opt['content'] = $http->get($opt['import']); if($opt['content'] === false) { $renderer->cdata('Failed to fetch remote CSV data'); return true; } } else { $renderer->info['cache'] = false; - if(auth_quickaclcheck(getNS($opt['file']).':*') < AUTH_READ) { + if(auth_quickaclcheck(getNS($opt['import']).':*') < AUTH_READ) { $renderer->cdata('Access denied to CSV data'); return true; } else { - $file = mediaFN($opt['file']); + $file = mediaFN($opt['import']); $opt['content'] = io_readFile($file); } } @@ -128,6 +130,30 @@ function render($mode, Doku_Renderer &$renderer, $opt) { return true; } + // Export the csv file + $targetfile = ''; + $export = $opt['export']; + if ( $export != '' ) { + $targetfile = htmlspecialchars(trim($export)); + if ( auth_quickaclcheck(getNS($targetfile.':*')) < AUTH_EDIT) { + $renderer->cdata('Access denied: Could not create download link.'); + $targetfile = ''; + return true; + } else { + $file = mediaFN($targetfile); + if ( file_put_contents ($file, $content, LOCK_EX) > 0 ) { + $linkname = $opt['linkname']; + if ( $linkname == '' ) + $linkname = 'Download CSV file'; + } + else { + $targetfile = ''; + $renderer->cdata('Failed to write '.$file.': Could not create download link.'); + return true; + } + } + } + // get the first row - it will define the structure $row = $this->csv_explode_row($content, $opt['delim'], $opt['enclosure'], $opt['escape']); $maxcol = count($row); @@ -186,6 +212,9 @@ function render($mode, Doku_Renderer &$renderer, $opt) { } $renderer->table_close(); + if ( $targetfile != '' ) + $renderer->internalmedia($targetfile, $linkname); + return true; } From 9be4aa9df7a519916081df6444313d666b2868e5 Mon Sep 17 00:00:00 2001 From: Roland Wunderling Date: Tue, 29 Sep 2015 19:40:41 +0200 Subject: [PATCH 2/3] use ' ' as option separator instead of '|' Options including spaces must be enclosed in quotes ". Also revert back to using file= instead of import=. --- syntax.php | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/syntax.php b/syntax.php index 2b4c9b5..3c5da53 100644 --- a/syntax.php +++ b/syntax.php @@ -59,7 +59,7 @@ function handle($match, $state, $pos, Doku_Handler &$handler) { 'hdr_rows' => 1, 'hdr_cols' => 0, 'span_empty_cols' => 0, - 'import' => '', + 'file' => '', 'export' => '', 'linkname' => 'Download CSV file', 'delim' => ',', @@ -72,20 +72,40 @@ function handle($match, $state, $pos, Doku_Handler &$handler) { unset($match); // parse options - $optsin = explode('|', $optstr); + $optsin = explode(' ', $optstr); + $populate = ''; foreach($optsin as $o) { - $o = trim($o); - if(preg_match('/(\w+)=(.*)/', $o, $matches)) { - $opt[$matches[1]] = $matches[2]; - } elseif($o) { + $o = trim($o); + if ( $populate != '' ) { + // handle closing quote + $opt[$populate] .= ' '; + if ( substr($o, -1) == '"' ) { + $o = substr($o, 0, -1); + $opt[$populate] .= $o; + $populate = ''; + } + else + $opt[$populate] .= $o; + } + elseif (preg_match('/(\w+)=(.*)/', $o, $matches)) { + // strip leading quote + if ( substr($matches[2], 0, 1) == '"' ) { + $matches[2] = substr($matches[2], 1, -1); + if ( substr($matches[2], -1) == '"' ) + $matches[2] = substr($matches[2], 0, -1); + else + $populate = $matches[1]; + } + $opt[$matches[1]] = $matches[2]; + } elseif($o) { if(preg_match('/^https?:\/\//i', $o)) { - $opt['import'] = $o; + $opt['file'] = $o; } else { - $opt['import'] = cleanID($o); - if(!strlen(getNS($opt['import']))) - $opt['import'] = $INFO['namespace'].':'.$opt['import']; + $opt['file'] = cleanID($o); + if(!strlen(getNS($opt['file']))) + $opt['file'] = $INFO['namespace'].':'.$opt['file']; } - } + } } if($opt['delim'] == 'tab') $opt['delim'] = "\t"; @@ -99,22 +119,22 @@ function render($mode, Doku_Renderer &$renderer, $opt) { if($mode == 'metadata') return false; // load file data - if($opt['import']) { - if(preg_match('/^https?:\/\//i', $opt['import'])) { + if($opt['file']) { + if(preg_match('/^https?:\/\//i', $opt['file'])) { require_once(DOKU_INC.'inc/HTTPClient.php'); $http = new DokuHTTPClient(); - $opt['content'] = $http->get($opt['import']); + $opt['content'] = $http->get($opt['file']); if($opt['content'] === false) { $renderer->cdata('Failed to fetch remote CSV data'); return true; } } else { $renderer->info['cache'] = false; - if(auth_quickaclcheck(getNS($opt['import']).':*') < AUTH_READ) { + if(auth_quickaclcheck(getNS($opt['file']).':*') < AUTH_READ) { $renderer->cdata('Access denied to CSV data'); return true; } else { - $file = mediaFN($opt['import']); + $file = mediaFN($opt['file']); $opt['content'] = io_readFile($file); } } From da31f0b82381fa8c8a7cf9dc05d7c80a30be6128 Mon Sep 17 00:00:00 2001 From: Roland Wunderling Date: Tue, 29 Sep 2015 20:08:38 +0200 Subject: [PATCH 3/3] fixed indentation --- syntax.php | 72 +++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/syntax.php b/syntax.php index 3c5da53..15f8445 100644 --- a/syntax.php +++ b/syntax.php @@ -75,29 +75,29 @@ function handle($match, $state, $pos, Doku_Handler &$handler) { $optsin = explode(' ', $optstr); $populate = ''; foreach($optsin as $o) { - $o = trim($o); - if ( $populate != '' ) { - // handle closing quote - $opt[$populate] .= ' '; - if ( substr($o, -1) == '"' ) { - $o = substr($o, 0, -1); - $opt[$populate] .= $o; - $populate = ''; - } - else - $opt[$populate] .= $o; - } - elseif (preg_match('/(\w+)=(.*)/', $o, $matches)) { - // strip leading quote - if ( substr($matches[2], 0, 1) == '"' ) { - $matches[2] = substr($matches[2], 1, -1); - if ( substr($matches[2], -1) == '"' ) - $matches[2] = substr($matches[2], 0, -1); - else - $populate = $matches[1]; - } - $opt[$matches[1]] = $matches[2]; - } elseif($o) { + $o = trim($o); + if ( $populate != '' ) { + // handle closing quote + $opt[$populate] .= ' '; + if ( substr($o, -1) == '"' ) { + $o = substr($o, 0, -1); + $opt[$populate] .= $o; + $populate = ''; + } + else + $opt[$populate] .= $o; + } + elseif (preg_match('/(\w+)=(.*)/', $o, $matches)) { + // strip leading quote + if ( substr($matches[2], 0, 1) == '"' ) { + $matches[2] = substr($matches[2], 1, -1); + if ( substr($matches[2], -1) == '"' ) + $matches[2] = substr($matches[2], 0, -1); + else + $populate = $matches[1]; + } + $opt[$matches[1]] = $matches[2]; + } elseif($o) { if(preg_match('/^https?:\/\//i', $o)) { $opt['file'] = $o; } else { @@ -105,7 +105,7 @@ function handle($match, $state, $pos, Doku_Handler &$handler) { if(!strlen(getNS($opt['file']))) $opt['file'] = $INFO['namespace'].':'.$opt['file']; } - } + } } if($opt['delim'] == 'tab') $opt['delim'] = "\t"; @@ -160,17 +160,17 @@ function render($mode, Doku_Renderer &$renderer, $opt) { $targetfile = ''; return true; } else { - $file = mediaFN($targetfile); - if ( file_put_contents ($file, $content, LOCK_EX) > 0 ) { - $linkname = $opt['linkname']; - if ( $linkname == '' ) - $linkname = 'Download CSV file'; - } - else { - $targetfile = ''; - $renderer->cdata('Failed to write '.$file.': Could not create download link.'); - return true; - } + $file = mediaFN($targetfile); + if ( file_put_contents ($file, $content, LOCK_EX) > 0 ) { + $linkname = $opt['linkname']; + if ( $linkname == '' ) + $linkname = 'Download CSV file'; + } + else { + $targetfile = ''; + $renderer->cdata('Failed to write '.$file.': Could not create download link.'); + return true; + } } } @@ -233,7 +233,7 @@ function render($mode, Doku_Renderer &$renderer, $opt) { $renderer->table_close(); if ( $targetfile != '' ) - $renderer->internalmedia($targetfile, $linkname); + $renderer->internalmedia($targetfile, $linkname); return true; }