1<?php
2
3use dokuwiki\Extension\AdminPlugin;
4use dokuwiki\File\PageResolver;
5
6/**
7 * Plugin CSVtoDWPages: Convert CSV file to DW Pages
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author     Ninja Dave <dokuwiki [at] reprah [dot] com>
11 */
12
13/**
14 * All DokuWiki plugins to extend the admin function
15 * need to inherit from this class
16 */
17class admin_plugin_csvtodwpages extends AdminPlugin
18{
19    /**
20     * Version: see lang.php
21     */
22
23    protected $params;
24    protected $paramcount;
25
26    private $nl = "\n";
27
28    /**
29     * access for managers
30     */
31    function forAdminOnly()
32    {
33        return false;
34    }
35
36    /**
37     * return sort order for position in admin menu
38     */
39    function getMenuSort()
40    {
41        return 1;
42    }
43
44    /**
45     * handle user request
46     */
47    function handle()
48    {
49        global $INPUT;
50        global $params;
51        global $paramcount;
52
53        $fieldnames = array('csvdw_namespace','csvdw_delim','csvdw_pagename','csvdw_csvdata','csvdw_csvtemplate','csvdw_overwrite','csvdw_dummyrun' ,'csvdw_trim');
54
55        if ($INPUT->has('csvdw_submit') && checkSecurityToken()) {
56
57            foreach ($fieldnames as $key) {
58                if (!blank($INPUT->param($key))){
59                    $paramcount++;
60                    $params[$key] = $INPUT->param($key);
61                }
62            }
63        }
64    }
65
66    /**
67     * output appropriate html
68     */
69    function html()
70    {
71        global $params;
72        global $paramcount;
73
74        $fieldcount = 0;
75
76        // update the label when the namespace is changed
77        echo "<script language='javascript'>function csvtodw_nslabel(){document.getElementById('csvtodw_nslabel').innerHTML=document.getElementById('csvtodw_ns').value;};</script>";
78
79        echo $this->locale_xhtml('intro') . $this->nl;
80        echo '<form method="post" action="" class="csvtodwpages">' . $this->nl;
81        echo '<input type="hidden" name="do" value="admin"/>' . $this->nl;
82        echo '<input type="hidden" name="page" value="'.$this->getPluginName().'"/>' . $this->nl;
83        formSecurityToken();
84
85        echo '<fieldset class="csvtodwpages">' . $this->nl;
86
87        echo '<table class="csvtodwpages"><tr><td>' . $this->nl;
88
89        echo '<h4>'.$this->getLang('csvdw_namespace').'</h4>' . $this->nl;
90        echo '<input type="text" name="csvdw_namespace" value="'.$this->_field('csvdw_namespace','').'" id="csvtodw_ns" onblur="csvtodw_nslabel()"/>' . $this->nl;
91        $fieldcount++;
92
93        echo '</td><td>' . $this->nl;
94
95        echo '<h4>'.$this->getLang('csvdw_pagename').'</h4>' . $this->nl;
96        echo '<input type="text" name="csvdw_pagename" value="'.$this->_field('csvdw_pagename','').'"/>' . $this->nl;
97        $fieldcount++;
98
99        echo '</td><td>' . $this->nl;
100
101        echo '<h4>'.$this->getLang('csvdw_delim').'</h4>' . $this->nl;
102        echo '<input type="text" name="csvdw_delim" maxlength="1" value="'.$this->_field('csvdw_delim',',').'"/>' . $this->nl;
103        $fieldcount++;
104
105        echo '</td></tr></table>' . $this->nl;
106
107        echo '<h4>'.$this->getLang('csvdw_csvtemplate').'</h4>' . $this->nl;
108        echo '<textarea name="csvdw_csvtemplate" class="edit">'.$this->_field('csvdw_csvtemplate').'</textarea><br/><br/>' . $this->nl;
109        $fieldcount++;
110
111        echo '<h4>'.$this->getLang('csvdw_csvdata').'</h4>' . $this->nl;
112        echo '<textarea name="csvdw_csvdata" class="edit">'.$this->_field('csvdw_csvdata').'</textarea><br/><br/>' . $this->nl;
113        $fieldcount++;
114
115        echo '<table class="csvtodwpages"><tr><td>' . $this->nl;
116
117        echo '<h4>'.sprintf($this->getLang('csvdw_overwrite'), $this->_field('csvdw_namespace','csvexploded')).'</h4>' . $this->nl;
118        echo '<input type="radio" class="csvtodwpages" name="csvdw_overwrite[]" value="true" id="owt" '.$this->_field('csvdw_overwrite','true',true).'/> ' . $this->nl;
119        echo '<label class="csvtodwpages" for="owt">'.$this->getLang('csvdw_yes').'</label> --- <label class="csvtodwpages" for="owf">'.$this->getLang('csvdw_no').'</label>' . $this->nl;
120        echo '<input type="radio" class="csvtodwpages" name="csvdw_overwrite[]" value="false" id="owf" '.$this->_field('csvdw_overwrite','false',true).'/>' . $this->nl;
121        $fieldcount++;
122
123        echo '</td><td>' . $this->nl;
124
125        echo '<h4>'.$this->getLang('csvdw_dummyrun').'</h4>' . $this->nl;
126        echo '<input type="radio" class="csvtodwpages" name="csvdw_dummyrun[]" value="true" id="drt" '.$this->_field('csvdw_dummyrun','true',true).'/> ' . $this->nl;
127        echo '<label class="csvtodwpages" for="drt">'.$this->getLang('csvdw_yes').'</label> --- <label class="csvtodwpages" for="drf">'.$this->getLang('csvdw_no').'</label>' . $this->nl;
128        echo '<input type="radio" class="csvtodwpages" name="csvdw_dummyrun[]" value="false" id="drf" '.$this->_field('csvdw_dummyrun','false',true).'/>' . $this->nl;
129        $fieldcount++;
130
131        echo '</td><td>' . $this->nl;
132
133        echo '<h4>'.$this->getLang('csvdw_trim').'</h4>' . $this->nl;
134        echo '<input type="radio" name="csvdw_trim[]" value="true" id="trimt" '.$this->_field('csvdw_trim','true',true).'/> ' . $this->nl;
135        echo '<label class="csvtodwpages" for="trimt">'.$this->getLang('csvdw_yes').'</label> --- <label class="csvtodwpages" for="trimf">'.$this->getLang('csvdw_no').'</label>' . $this->nl;
136        echo '<input type="radio" name="csvdw_trim[]" value="false" id="trimf" '.$this->_field('csvdw_trim','false',true).'/>' . $this->nl;
137        $fieldcount++;
138
139        echo '</td></tr></table>' . $this->nl;
140
141        // required fields warning
142		echo (is_numeric($paramcount) && $paramcount !== $fieldcount) ? "<span class='csvtodwpageswarning'>".$this->getLang('csvdw_allfields')."</span><br/>" : '' . $this->nl;
143
144        echo '<input type="submit" name="csvdw_submit" class="csvtodwpages" value="'.$this->getLang('csvdw_submitbtn').'"/>' . $this->nl;
145
146        echo '<div class="csvtodwpages_ver">'.$this->getLang('csvdw_ver').'</div>';
147        echo '</fieldset>';
148
149        echo '</form>';
150
151        // if all form values recieved, let's explode!
152        if ($paramcount == $fieldcount) {
153        	$this->_csvexploder();
154
155		} elseif ($paramcount > $fieldcount) {
156            // something weird going on
157            echo '<br/><fieldset><span class="csvtodwpageswarning">Incorrect number of form parameters received. We\'re screwed!<br/>Needed '.$fieldcount.', got '.$paramcount.'</span></fieldset>' . $this->nl;
158        }
159	}
160
161
162    /**
163     * take form values and explode the submitted csvdata into dokuwiki pages based on submitted csvtemplate
164     */
165	function _csvexploder()
166    {
167        global $conf;       // get dokuwiki conf array
168        global $params;
169        global $paramcount;
170        global $ID;
171
172        $resolver = new PageResolver($ID);
173
174
175        $namespace      = strtolower($params['csvdw_namespace']); // which namespace to create pages within
176
177        $pagename_column = $params['csvdw_pagename']; // which column(s) in the csv file used for the pagename
178
179        $indexpage      = (empty($conf['start'])) ? 'start' : $conf['start']; // the dokuwiki index page name
180
181        $csvdata        = $params['csvdw_csvdata'];         // the dokuwiki page in which you pasted the csv data
182        $csvtemplate    = $params['csvdw_csvtemplate'];     // the dokuwiki page you created holding the template
183        $csvdelim       = $params['csvdw_delim'][0];        // the csv delimiter character (single character)
184
185        $overwrite_files = ($params['csvdw_overwrite'][0] == 'true') ? TRUE : FALSE;  // set this to TRUE to overwrite existing pages
186        $dummyrun        = ($params['csvdw_dummyrun'][0]  == 'true') ? TRUE : FALSE;  // is this just a dummy run?
187        $trimspaces      = ($params['csvdw_trim'][0]      == 'true') ? TRUE : FALSE;  // is this just a dummy run?
188
189        $indexpagecontent = $this->getLang('csvdw_indexpagecontent');   // template for autogenerated index page
190
191        // $dr = rtrim( $_SERVER['DOCUMENT_ROOT'], "/" ) .'/';
192
193        // get field names from first row
194        $csvfieldnames = str_getcsv(strtok($csvdata, PHP_EOL), $csvdelim);
195
196        // if we have some csvfieldnames lets rock'n'roll
197        if ($csvfieldnames !== FALSE) {
198
199            if ($dummyrun) {
200                echo $this->getLang('csvdw_dummyrunnotice') . $this->nl;
201            }
202
203            echo $this->getLang('csvdw_explodednotice') . $this->nl;
204
205            if (array_count_values($csvfieldnames) >= 1) {
206
207                while (($line = strtok(PHP_EOL)) !== FALSE) {
208
209                    $line = str_getcsv($line, $csvdelim);
210
211                    if (!empty(array_filter($line))) {
212
213                        // get clean copy of the template
214                        $thispage = $csvtemplate;
215
216                        // set the page name for the index page text
217                        $pagename = $this->_get_filename($pagename_column, $line);
218
219                        // clean output filename to dokuwiki standards
220                        $cleanpagename = cleanID($pagename);
221
222                        $indexpagecontent .= "  * [[$cleanpagename|$pagename]]\r\n";
223
224                        // replace the template [placeholder] names with content
225                        foreach ($csvfieldnames as $key => $field) {
226                            $thispage = str_replace('['.strtolower($field).']', $trimspaces ? trim($line[$key]) : $line[$key], $thispage);
227                        }
228
229                        // create wiki page
230                        $filecreated = FALSE;
231
232                        if ($overwrite_files !== TRUE && page_exists($resolver->resolveId($namespace.':'.$cleanpagename))) {
233                            echo $this->getLang('csvdw_skipnoticep');   // msg($this->getLang('csvdw_skipnotice'));
234                        } else {
235                            if ($dummyrun) {
236                                $filecreated = TRUE;
237                            } else {
238                                // we are assuming that if the file exists after saveWikiText() then it was created
239                                // Unfortunately saveWikiText() doesn't provide a return value to indicate if it succeeded or failed.
240                                // This leaves it possible that a page existed previously but we were unable to overwrite it.
241                                // The DW notice system will put a notice in the exploded file list to indicate the failure though.
242                                // see io_writeWikiPage() and _io_writeWikiPage_action() in .\dokuwiki\inc\io.php
243                                saveWikiText($resolver->resolveId($namespace.':'.$cleanpagename), $thispage, $this->getLang('csvdw_summary'));
244                                $filecreated = page_exists($resolver->resolveId($namespace.':'.$cleanpagename));
245                            }
246
247                            if ($filecreated == FALSE) {
248                                echo $this->getLang('csvdw_errornotice');
249                            } else {
250                                echo $this->getLang('csvdw_creatednoticep');
251                            }
252                        }
253
254                        echo wikiFN($resolver->resolveId($namespace.':'.$cleanpagename))."<br/>" . $this->nl;
255                    }
256
257                }
258
259                // show an example template page if this is a dummy run
260                // (uses the data from the last processed csv row)
261                if ($dummyrun) {
262                    echo $this->getLang('csvdw_examplepagenotice') . $this->nl;
263                    echo nl2br($thispage) . $this->nl;
264                }
265
266                echo "<br/><br/><hr/>" . $this->nl;
267
268                // create index page
269                $filecreated = FALSE;
270
271                if ($overwrite_files !== TRUE && page_exists($resolver->resolveId($namespace.':'.$indexpage))) {
272                    echo $this->getLang('csvdw_skipnoticei');   // msg($this->getLang('csvdw_skipnoticei'));
273
274                } else {
275                    if ($dummyrun) {
276                        $filecreated = TRUE;
277                    } else {
278                        saveWikiText($resolver->resolveId($namespace.':'.$indexpage), $indexpagecontent, $this->getLang('csvdw_summary'));
279                        $filecreated = page_exists($resolver->resolveId($namespace.':'.$indexpage));
280                    }
281                    if ($filecreated == FALSE) {
282                        echo $this->getLang('csvdw_errornotice');
283                    } else {
284                        echo $this->getLang('csvdw_creatednoticei');
285                    }
286                }
287
288                // echo wikiFN(resolve_id($namespace, $indexpage))."<br/>" . $this->nl;
289                echo wikiFN($resolver->resolveId($namespace.':'.$indexpage))."<br/>" . $this->nl;
290            }
291
292            echo "<br/>" . $this->nl;
293
294            // null is used as the first param to wl() as it's the only way to get the URL
295            // to work on both our dev server (excludes the doku.php in url) and the live
296            // server (includes doku.php in url)
297            echo 'Go to <a href="' . wl(null, '', false) . $resolver->resolveId($namespace.':'.$indexpage) . '">' . $namespace . ':' . $indexpage . '</a><br/>' . $this->nl;
298        }
299
300        strtok('',''); // release strtok memory
301	}
302
303    /**
304     * Helper to build a unique pagename from specified csv column data
305     *
306     * Will combine multiple column data together if comma separated list
307     * is supplied. Each field will be separated with underscores _
308     *
309     * @param $pagecols string Single int value or comma separated list eg. 0,3
310     * @param $line     array  An array containing the csv values for the current row
311     * @return string   A page name combining all row values separated with _ eg. youngstown_oh
312     */
313    protected function _get_filename($pagecols='', &$line)
314    {
315        $pagename = '';
316        $pagecols = explode(',', $pagecols);
317
318        // combine column data into filename
319        foreach ($pagecols as $col) {
320            $pagename .= trim($line[$col]).'_';
321        }
322
323        return rtrim($pagename, "_");
324    }
325
326    /**
327     * Helper to repopulate form field values from POST data
328     *
329     * @param  $field   = fieldname
330     * @param  $default = default value to return if no value present
331     * @param  $isradio = flag indicating if field is a radiobutton - return 'checked' instead of value
332     * @return string : form field value or default
333     * @return array  : first element of array if incoming field was an array (multivalue field)
334     */
335	protected function _field($field, $default='', $isradio=false)
336    {
337        global $params;
338        global $paramcount;
339
340        $val = '';
341        if (!blank($params[$field])) {
342            $val = is_array($params[$field]) ? $params[$field][0] : $params[$field];
343        }
344
345        if ($isradio) {
346            if ($val==$default) {
347                return 'checked';
348            }
349            return '';
350        }
351
352        return !blank($val) ? $val : $default;
353    }
354
355}
356
357