1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Gerrit Uitslag <klapinklapin@gmail.com>
5 */
6
7use dokuwiki\Extension\Event;
8
9/**
10 * Take care of exporting only pages in selection, and not the bookmanager page itself
11 */
12class action_plugin_bookcreator_export extends DokuWiki_Action_Plugin {
13
14    /**
15     * Registers a callback function for a given event
16     *
17     * @param Doku_Event_Handler $controller
18     */
19    public function register(Doku_Event_Handler $controller) {
20        $controller->register_hook('ACTION_EXPORT_POSTPROCESS', 'BEFORE', $this, 'replacePageExportBySelection');
21        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'renameDoExportNSAction');
22    }
23
24    /**
25     * Handle export request for exporting namespace renamed as normal html/text
26     * export, the 'book_ns' is used to recognize the namespace. This way we can use the default handling of text
27     * and html exports
28     *
29     * @param Event $event
30     */
31    public function renameDoExportNSAction(Event $event) {
32        $allowedEvents = ['export_htmlns', 'export_textns'];
33        if(in_array($event->data, $allowedEvents)) {
34            $event->data = substr($event->data, 0, -2);
35        }
36
37        //export_xhtml is built-in xhtml export with header
38        if($event->data === 'export_html') { // also act_clean() does this rename
39            $event->data = 'export_xhtml';
40        }
41    }
42
43    /**
44     * Handle export request for exporting the selection as html or text
45     *
46     * @param Event $event
47     */
48    public function replacePageExportBySelection(Event $event) {
49        if(!in_array($event->data['mode'], ['text', 'xhtml'])) {
50            //skip other export modes
51            return;
52        }
53
54        global $ID;
55        global $INPUT;
56        try{
57            if($INPUT->has('selection')) {
58                //export current list from the bookmanager
59                $list = json_decode($INPUT->str('selection', '', true), true);
60                if(!is_array($list) || empty($list)) {
61                    throw new Exception($this->getLang('empty'));
62                }
63            } elseif($INPUT->has('savedselection')) {
64                //export a saved selection of the Bookcreator Plugin
65                /** @var action_plugin_bookcreator_handleselection $SelectionHandling */
66                $SelectionHandling = plugin_load('action', 'bookcreator_handleselection');
67                $savedselection = $SelectionHandling->loadSavedSelection($INPUT->str('savedselection'));
68                $list = $savedselection['selection'];
69            } elseif($INPUT->has('book_ns')) {
70                //export triggered with export_textns or export_htmlns
71                $list = $this->collectPagesOfNS();
72            } else {
73                //export is not from bookcreator
74                return;
75            }
76
77            //remove default export version of current page
78            $event->data['output'] = '';
79
80            $skippedpages = array();
81            foreach($list as $index => $pageid) {
82                if(auth_quickaclcheck($pageid) < AUTH_READ) {
83                    $skippedpages[] = $pageid;
84                    unset($list[$index]);
85                }
86            }
87            $list = array_filter($list, 'strlen'); //use of strlen() callback prevents removal of pagename '0'
88
89            //if selection contains forbidden pages throw (overridable) warning
90            if(!$INPUT->bool('book_skipforbiddenpages') && !empty($skippedpages)) {
91                $msg = hsc(join(', ', $skippedpages));
92                throw new Exception(sprintf($this->getLang('forbidden'), $msg));
93            }
94        } catch (Exception $e) {
95            http_status(400);
96            print $e->getMessage();
97            exit();
98        }
99
100        $keep = $ID;
101        foreach($list as $page) {
102            $ID = $page;
103            $event->data['output'] .= p_cached_output(wikiFN($page), $event->data['mode'], $page);
104        }
105        $ID = $keep;
106    }
107
108
109    /**
110     * @return array list of pages from ns after filtering
111     * @throws Exception
112     */
113    private function collectPagesOfNS(): array
114    {
115        global $INPUT, $conf;
116        //check input for ns
117        $pdfnamespace = cleanID($INPUT->str('book_ns'));
118        if (!@is_dir(dirname(wikiFN($pdfnamespace . ':dummy')))) {
119            throw new Exception($this->getLang('needns'));
120        }
121
122        //sort order
123        $order = $INPUT->str('book_order', 'natural', true);
124        $sortoptions = array('pagename', 'date', 'natural');
125        if (!in_array($order, $sortoptions)) {
126            $order = 'natural';
127        }
128
129        //search depth
130        $depth = $INPUT->int('book_nsdepth', 0);
131        if ($depth < 0) {
132            $depth = 0;
133        }
134
135        //page search
136        $result = array();
137        $opts = array('depth' => $depth); //recursive all levels
138        $dir = utf8_encodeFN(str_replace(':', '/', $pdfnamespace));
139        search($result, $conf['datadir'], 'search_allpages', $opts, $dir);
140
141        // exclude ids
142        $excludes = $INPUT->arr('excludes');
143        if (!empty($excludes)) {
144            $result = array_filter($result, function ($item) use ($excludes) {
145                return !in_array($item['id'], $excludes);
146            });
147        }
148        // exclude namespaces
149        $excludesns = $INPUT->arr('excludesns');
150        if (!empty($excludesns)) {
151            $result = array_filter($result, function ($item) use ($excludesns) {
152                foreach ($excludesns as $ns) {
153                    if (strpos($item['id'], $ns . ':') === 0) return false;
154                }
155                return true;
156            });
157        }
158
159        //sorting
160        if (count($result) > 0) {
161            if ($order == 'date') {
162                usort($result, array($this, '_datesort'));
163            } elseif ($order == 'pagename' || $order == 'natural') {
164                usort($result, array($this, '_pagenamesort'));
165            }
166        }
167
168        $list = [];
169        foreach ($result as $item) {
170            $list[] = $item['id'];
171        }
172
173        if ($pdfnamespace !== '') {
174            if (!in_array($pdfnamespace . ':' . $conf['start'], $list, true)) {
175                if (file_exists(wikiFN(rtrim($pdfnamespace, ':')))) {
176                    array_unshift($list, rtrim($pdfnamespace, ':'));
177                }
178            }
179        }
180        return $list;
181    }
182}
183