1<?php
2/**
3 * BookCreator plugin : Create a book from some pages.
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Luigi Micco <l.micco@tiscali.it>
7 */
8
9// must be run within Dokuwiki
10use dokuwiki\Form\Form;
11
12/**
13 * All DokuWiki plugins to extend the parser/rendering mechanism
14 * need to inherit from this class
15 */
16class syntax_plugin_bookcreator_bookmanager extends DokuWiki_Syntax_Plugin {
17
18    /** @var helper_plugin_bookcreator */
19    protected $hlp;
20
21    /**
22     * Constructor
23     */
24    public function __construct() {
25        $this->hlp = plugin_load('helper', 'bookcreator');
26    }
27    /**
28     * @param string $mode
29     */
30    function connectTo($mode) {
31        $this->Lexer->addSpecialPattern('~~\w*?BOOK.*?~~', $mode, 'plugin_bookcreator_bookmanager');
32    }
33
34    /**
35     * Syntax Type
36     *
37     * @return string
38     */
39    function getType() {
40        return 'container';
41    }
42
43    /**
44     * Paragraph Type
45     *
46     * @return string
47     */
48    function getPType() {
49        return 'block';
50    }
51
52    /**
53     * Where to sort in?
54     */
55    function getSort() {
56        return 190;
57    }
58
59    /**
60     * Handler to prepare matched data for the rendering process
61     *
62     * @param   string       $match   The text matched by the patterns
63     * @param   int          $state   The lexer state for the match
64     * @param   int          $pos     The character position of the matched text
65     * @param   Doku_Handler $handler The Doku_Handler object
66     * @return  array Return an array with all data you want to use in render, false don't add an instruction
67     */
68    function handle($match, $state, $pos, Doku_Handler $handler) {
69
70        $match = substr($match, 2, -2); // strip markup
71        if(substr($match, 0, 7) == 'ARCHIVE') {
72            $type = 'archive';
73        } else {
74            $type = 'bookmanager';
75        }
76
77        $num   = 10;
78        $order = 'date';
79        if($type == 'archive') {
80            [/* $junk */, $params] = array_pad(explode(':', $match, 2), 2, '');
81            [$param1, $param2] = array_pad(explode('&', $params, 2),2, '');
82
83            $sortoptions = ['date', 'title'];
84            if(is_numeric($param1)) {
85                $num = (int) $param1;
86                if(in_array($param2, $sortoptions)) {
87                    $order = $param2;
88                }
89            } elseif(in_array($param1, $sortoptions)) {
90                $order = $param1;
91                if(is_numeric($param2)) {
92                    $num = (int)$param2;
93                }
94            } elseif(is_numeric($param2)) {
95                $num = (int) $param2;
96            }
97        }
98
99        return array($type, $num, $order);
100
101    }
102
103    /**
104     * @param string        $format render mode e.g. text, xhtml, meta,...
105     * @param Doku_Renderer $renderer
106     * @param array         $data return of handle()
107     * @return bool
108     */
109    function render($format, Doku_Renderer $renderer, $data) {
110        global $ID;
111        global $INPUT;
112
113        list($type, $num, $order) = $data;
114
115        if($type == "bookmanager") {
116            if($format == 'xhtml') {
117                /** @var Doku_Renderer_xhtml $renderer */
118                $renderer->info['cache'] = false;
119
120                // verification that if the user can save / delete the selections
121                $usercansave = (auth_quickaclcheck($this->getConf('save_namespace').':*') >= AUTH_CREATE);
122
123                //intervents the normal export_* handling
124                $do = $INPUT->str('do');
125                $ignore_onscreen_exports = [
126                    'export_html',
127                    'export_htmlns'
128                ];
129                if(in_array($do, $ignore_onscreen_exports)) {
130                    //export as xhtml or text, is handled in action component 'export'
131                    return false;
132                }
133
134                //show the bookmanager
135                $this->showBookManager($renderer, $usercansave);
136
137                // Displays the list of saved selections
138                $this->renderSelectionslist($renderer, true, $ID, $order);
139                $renderer->doc .= "<br />";
140            }
141
142        } else {
143            // type == archive
144
145            if($format == 'xhtml') {
146                /** @var Doku_Renderer_xhtml $renderer */
147                // generates the list of saved selections
148                $this->renderSelectionslist($renderer, false, $this->getConf('book_page'), $order, $num);
149            }
150        }
151        return false;
152    }
153
154    /**
155     * Generates the list of save selections
156     *
157     * @param Doku_Renderer_xhtml $renderer
158     * @param bool                $bookmanager whether this list is displayed in the Book Manager
159     * @param string              $bmpage pageid of the page with the Book Manager
160     * @param string              $order sort by 'title' or 'date'
161     * @param int                 $num number of listed items, 0=all items
162     *
163     * if in the Book Manager, the delete buttons are displayed
164     * the list with save selections is only displayed once, and the bookmanager with priority
165     */
166    public function renderSelectionslist($renderer, $bookmanager, $bmpage, $order, $num = 0) {
167        $result = $this->getlist($order, $num);
168        if(sizeof($result) > 0) {
169            $form = new Form(['action'=> wl($bmpage)]);
170            $form->addClass('bookcreator__selections__list');
171
172            if($bookmanager) {
173                $form->addFieldsetOpen($this->getLang('listselections'));
174                $form->addHTML('<div class="message"></div>');
175            }
176            $form->addHTML($this->showlist($result, $bookmanager));
177            $form->setHiddenField('do', '');
178            $form->setHiddenField('task', '');
179            $form->setHiddenField('page', '');
180            if($bookmanager) {
181                $form->addFieldsetClose();
182            }
183
184            $renderer->doc .= $form->toHTML();
185        }
186    }
187
188    /**
189     * Displays the Bookmanager - Let organize selections and export them
190     * Only visible when a selection is loaded from the save selections or from cookie FIXME
191     *
192     * @param Doku_renderer_xhtml $renderer
193     * @param bool                $usercansave User has permissions to save the selection
194     */
195    private function showBookManager($renderer, $usercansave) {
196        global $ID;
197//        $title = '';
198
199        //start main container - open left column
200        $renderer->doc .= "<div class='bookcreator__manager'>";
201        // Display pagelists
202        // - selected pages
203        $renderer->doc .= "<div class='bookcreator__pagelist' >";
204        $this->showPagelist($renderer, 'selected');
205        $renderer->doc .= "<br />";
206
207        // Add namespace to selection
208
209        $form = new dokuwiki\Form\Form();
210        $form->addClass('selectnamespace');
211        $form->addButton('selectns', $this->getLang('select_namespace'))
212            ->attr('type', 'submit');
213
214        $renderer->doc .= "<div class='bookcreator__selectns'>";
215        $renderer->doc .= $form->toHTML();
216        $renderer->doc .= "</div>";
217
218        // - excluded pages
219        $renderer->doc .= '<div id="bookcreator__delpglst">';
220        $this->showPagelist($renderer, 'deleted');
221        $renderer->doc .= '</div>';
222
223        // Reset current selection
224        $form = new Form();
225        $form->addClass('clearactive');
226        $form->addButton('resetselection', $this->getLang('reset'))
227            ->attr('type', 'submit');
228
229        $renderer->doc .= '<div>';
230        $renderer->doc .= $form->toHTML();
231        $renderer->doc .= '</div>';
232
233        //close left column - open right column
234        $renderer->doc .= "</div>";
235        $renderer->doc .= "<div class='bookcreator__actions'>";
236        // PDF Export
237        $values   = [
238            'export_html'=> $this->getLang('exportprint')
239        ];
240        $selected = 'export_html';
241        if(file_exists(DOKU_PLUGIN."text/renderer.php") && !plugin_isdisabled("text")) {
242            $values['export_text'] = $this->getLang('exporttext');
243        }
244        if(file_exists(DOKU_PLUGIN."odt/action/export.php") && !plugin_isdisabled("odt")) {
245            $values['export_odtbook'] = $this->getLang('exportodt');
246            $selected                 = 'export_odtbook';
247        }
248        if(file_exists(DOKU_PLUGIN."dw2pdf/action.php") && !plugin_isdisabled("dw2pdf")) {
249            $values['export_pdfbook'] = $this->getLang('exportpdf');
250            $selected                 = 'export_pdfbook';
251        }
252
253        $form = new Form();
254        $form->addClass('downloadselection');
255
256        $form->addFieldsetOpen($this->getLang('export'));
257
258        $form->addHTML($this->getLang('title')." ");
259        $form->addTextInput('book_title')
260            ->addClass('edit')
261            ->attrs(['size'=> 30]);
262        $form->addCheckbox('book_skipforbiddenpages', $this->getLang('skipforbiddenpages'))
263            ->addClass('book_skipforbiddenpages'); //note: class extra at input
264        $form->addDropdown('do', $values)
265            ->val($selected)
266            ->attrs(['size'=> 1]);
267        $form->setHiddenField('outputTarget', 'file');
268        $form->setHiddenField('id', $ID);
269        $form->addButton('exportselection', $this->getLang('create'))->attr('type', 'submit');
270
271        $form->addFieldsetClose();
272
273        $renderer->doc .= $form->toHTML();
274
275
276        // Save current selection to a wikipage
277        if($usercansave) {
278            $form = new Form();
279            $form->addClass('saveselection');
280
281
282            $form->addFieldsetOpen($this->getLang('saveselection'));
283
284            $form->addHTML('<div class="message"></div>');
285            $form->addTextInput('bookcreator_title')
286                ->addClass('edit');
287            $form->setHiddenField('task', 'save');
288            $form->addButton('saveselection', $this->getLang('save'))->attr('type', 'submit');
289
290            $form->addFieldsetClose();
291
292            $renderer->doc .= $form->toHTML();
293        }
294
295        //close containers
296        $renderer->doc .= '</div>'
297                        . "</div><div class='clearer'></div>"
298                        . "<br />";
299
300        $renderer->doc .= "<div id='preparing-file-modal' title='{$this->getLang("titlepreparedownload")}' style='display: none;'>"
301                        . $this->getLang('preparingdownload')
302                        . '    <div class="ui-progressbar-value ui-corner-left ui-corner-right" style="width: 100%; height:22px; margin-top: 20px;"></div>'
303                        . '</div>';
304
305        $renderer->doc .= "<div id='error-modal' title='{$this->getLang("titleerrordownload")}' style='display: none;'>"
306                        . "    <div class='downloadresponse'>{$this->getLang('faileddownload')}</div>"
307                        . '</div>';
308
309    }
310
311    /**
312     * Displays list of selected/deleted pages
313     *
314     * @param Doku_Renderer_xhtml $renderer
315     * @param string              $selection 'deleted' or 'selected'
316     */
317    private function showPagelist($renderer, $selection) {
318        if($selection == 'deleted') {
319            $id          = 'deletedpagelist';
320            $heading     = 'removed';
321        } else {
322            $id          = 'pagelist';
323            $heading     = 'toprint';
324        }
325
326        $renderer->header($this->getLang($heading), 2, 0);
327        $renderer->doc .= "<ul id=$id class='pagelist $selection'>";
328        $renderer->listu_close();
329    }
330
331    /**
332     * usort callback to sort by file lastmodified time
333     *
334     * @param array $a
335     * @param array $b
336     * @return int
337     */
338    function _datesort($a, $b) {
339        if($b['rev'] < $a['rev']) return -1;
340        if($b['rev'] > $a['rev']) return 1;
341        return strcmp($b['id'], $a['id']);
342    }
343
344    /**
345     * usort callback to sort by file title
346     *
347     * @param array $a
348     * @param array $b
349     * @return int
350     */
351    function _titlesort($a, $b) {
352        if($a['id'] < $b['id']) return -1;
353        if($a['id'] > $b['id']) return 1;
354        return 0;
355    }
356
357    /**
358     * Lists saved selections, by looking up corresponding pages in the reserverd namespace
359     *
360     * @param string $order sort by 'date' or 'title'
361     * @param int    $limit maximum number of selections, 0=all
362     * @return array
363     */
364    private function getlist($order, $limit = 0) {
365        global $conf;
366
367        $ns      = cleanID($this->getConf('save_namespace'));
368        $tt      = utf8_encodeFN(str_replace(':', '/', $ns));
369        $nsdepth = count(explode('/', $tt));
370        $result  = array();
371        $opts    = array(
372            'depth'   => $nsdepth + 1,
373            'skipacl' => false
374        );
375        $ns      = cleanID($this->getConf('save_namespace'));
376        $tt      = utf8_encodeFN(str_replace(':', '/', $ns));
377
378        search($result, $conf['datadir'], 'search_allpages', $opts, $tt);
379
380        if(sizeof($result) > 0) {
381
382            if($order == 'date') {
383                usort($result, array($this, '_datesort'));
384            } elseif($order == 'title') {
385                usort($result, array($this, '_titlesort'));
386            }
387
388            if($limit != 0) $result = array_slice($result, 0, $limit);
389        }
390        return $result;
391    }
392
393    /**
394     * Displays the Selection List
395     *
396     * @param array $result   results generated by search()
397     * @param bool  $isbookmanager
398     * @return string html of list
399     */
400    private function showlist($result, $isbookmanager = false) {
401        $output = '<ul>';
402        foreach($result as $item) {
403            $output .= $this->hlp->createListitem($item, $isbookmanager);
404        }
405        $output .= '</ul>';
406        return $output;
407    }
408}
409