1<?php
2/**
3 * Plugin nspages : Displays nicely a list of the pages of a namespace
4 *
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author  Guillaume Turri <guillaume.turri@gmail.com>
7 * @author  Daniel Schranz <xla@gmx.at>
8 * @author  Ignacio Bergmann
9 * @author  Andreas Gohr <gohr@cosmocode.de>
10 * @author  Ghassem Tofighi <ghassem@gmail.com>
11 */
12
13use dokuwiki\Utf8\PhpString;
14
15if(!defined('DOKU_INC')) die();
16require_once 'printers/printerLineBreak.php';
17require_once 'printers/printerOneLine.php';
18require_once 'printers/printerSimpleList.php';
19require_once 'printers/printerNice.php';
20require_once 'printers/printerPictures.php';
21require_once 'printers/printerTree.php';
22require_once 'fileHelper/fileHelper.php';
23require_once 'optionParser.php';
24require_once 'namespaceFinder.php';
25
26/**
27 * All DokuWiki plugins to extend the parser/rendering mechanism
28 * need to inherit from this class
29 */
30class syntax_plugin_nspages extends DokuWiki_Syntax_Plugin {
31    function connectTo($aMode) {
32        $this->Lexer->addSpecialPattern('<nspages[^>]*>', $aMode, 'plugin_nspages');
33    }
34
35    function getSort() {
36        //Execute before html mode
37        return 189;
38    }
39
40    function getType() {
41        return 'substition';
42    }
43
44    function handle($match, $state, $pos, Doku_Handler $handler) {
45        $return = $this->_getDefaultOptions();
46        $return['pos'] = $pos;
47
48        $match = PhpString::substr($match, 8, -1); //9 = strlen("<nspages")
49        $match .= ' ';
50
51        optionParser::checkOption($match, "subns", $return['subns'], true);
52        optionParser::checkOption($match, "nopages", $return['nopages'], true);
53        optionParser::checkOption($match, "simpleListe?", $return['simpleList'], true);
54        optionParser::checkOption($match, "tree", $return['tree'], true);
55        optionParser::checkOption($match, "numberedListe?", $return['numberedList'], true);
56        optionParser::checkOption($match, "simpleLineBreak", $return['lineBreak'], true);
57        optionParser::checkOption($match, "title", $return['title'], true);
58        optionParser::checkOption($match, "idAndTitle", $return['idAndTitle'], true);
59        optionParser::checkOption($match, "h1", $return['title'], true);
60        optionParser::checkOption($match, "simpleLine", $return['simpleLine'], true);
61        optionParser::checkOption($match, "sort(By)?Id", $return['sortid'], true);
62        optionParser::checkOption($match, "reverse", $return['reverse'], true);
63        optionParser::checkOption($match, "pagesinns", $return['pagesinns'], true);
64        optionParser::checkOption($match, "nat(ural)?Order", $return['natOrder'], true);
65        optionParser::checkOption($match, "sort(By)?Date", $return['sortDate'], true);
66        optionParser::checkOption($match, "sort(By)?CreationDate", $return['sortByCreationDate'], true);
67        optionParser::checkOption($match, "hidenopages", $return['hidenopages'], true);
68        optionParser::checkOption($match, "hidenons", $return['hidenons'], true);
69        optionParser::checkOption($match, "hidenosubns", $return['hidenosubns'], true);
70        optionParser::checkOption($match, "showhidden", $return['showhidden'], true);
71        optionParser::checkOption($match, "(use)?Pictures?", $return['usePictures'], true);
72        optionParser::checkOption($match, "(modification)?Dates?OnPictures?", $return['modificationDateOnPictures'], true);
73        optionParser::checkOption($match, "displayModificationDates?", $return["displayModificationDate"], true);
74        optionParser::checkOption($match, "includeItemsInTOC", $return["includeItemsInTOC"], true);
75        optionParser::checkOption($match, "sidebar", $return["sidebar"], true);
76        optionParser::checkRecurse($match, $return['maxDepth']);
77        optionParser::checkNbColumns($match, $return['nbCol']);
78        optionParser::checkSimpleStringArgument($match, $return['textPages'], $this, 'textPages');
79        optionParser::checkSimpleStringArgument($match, $return['customTitle'], $this, 'customTitle');
80        optionParser::checkSimpleStringArgument($match, $return['sortByMetadata'], $this, 'sortByMetadata');
81        optionParser::checkSimpleStringArgument($match, $return['textNS'], $this, 'textNS');
82        optionParser::checkDictOrder($match, $return['dictOrder'], $this);
83        optionParser::checkRegEx($match, "pregPages?On=\"([^\"]*)\"", $return['pregPagesOn']);
84        optionParser::checkRegEx($match, "pregPages?Off=\"([^\"]*)\"", $return['pregPagesOff']);
85        optionParser::checkRegEx($match, "pregPages?TitleOn=\"([^\"]*)\"", $return['pregPagesTitleOn']);
86        optionParser::checkRegEx($match, "pregPages?TitleOff=\"([^\"]*)\"", $return['pregPagesTitleOff']);
87        optionParser::checkRegEx($match, "pregNSOn=\"([^\"]*)\"", $return['pregNSOn']);
88        optionParser::checkRegEx($match, "pregNSOff=\"([^\"]*)\"", $return['pregNSOff']);
89        optionParser::checkRegEx($match, "pregNSTitleOn=\"([^\"]*)\"", $return['pregNSTitleOn']);
90        optionParser::checkRegEx($match, "pregNSTitleOff=\"([^\"]*)\"", $return['pregNSTitleOff']);
91        optionParser::checkNbItemsMax($match, $return['nbItemsMax']);
92        optionParser::checkGlobalExclude($this->getConf('global_exclude'), $return['excludedPages'], $return['excludedNS']);
93        optionParser::checkExclude($match, $return['excludedPages'], $return['excludedNS'], $return['excludeSelfPage']);
94        optionParser::checkAnchorName($match, $return['anchorName']);
95        optionParser::checkActualTitle($match, $return['actualTitleLevel']);
96        optionParser::checkSimpleStringArgument($match, $return['defaultPicture'], $this, 'defaultPicture');
97
98        //Now, only the wanted namespace remains in $match
99        $match = strtolower(trim($match));
100        if ($return["sidebar"]) {
101            // Don't bother resolving or sanitizing now: it will be done at render-time in this mode
102            $return['wantedNS'] = $match;
103        } else {
104            $nsFinder = new namespaceFinder($match);
105            $return['wantedNS'] = $nsFinder->getWantedNs();
106            $return['safe'] = $nsFinder->isNsSafe();
107            $return['wantedDir'] = $nsFinder->getWantedDirectory();
108        }
109
110        return $return;
111    }
112
113    private function _getDefaultOptions(){
114        return array(
115            'subns'         => false, 'nopages' => false, 'simpleList' => false, 'lineBreak' => false,
116            'excludedPages' => array(), 'excludedNS' => array(), 'excludeSelfPage' => false,
117            'title'         => false, 'wantedNS' => '', 'wantedDir' => '', 'safe' => true,
118            'textNS'        => '', 'textPages' => '', 'pregPagesOn' => array(),
119            'customTitle'   => null,
120            'pregPagesOff'  => array(), 'pregNSOn' => array(), 'pregNSOff' => array(),
121            'pregPagesTitleOn' => array(), 'pregPagesTitleOff' => array(),
122            'pregNSTitleOn' => array(), 'pregNSTitleOff' => array(),
123            'maxDepth'      => (int) 1, 'nbCol' => 3, 'simpleLine' => false,
124            'sortid'        => false, 'reverse' => false,
125            'pagesinns'     => false, 'anchorName' => null, 'actualTitleLevel' => false,
126            'idAndTitle'    => false, 'nbItemsMax' => 0, 'numberedList' => false,
127            'natOrder'      => false, 'sortDate' => false,
128            'hidenopages'   => false, 'hidenons' => false, 'hidenosubns' => false, 'usePictures' => false,
129            'showhidden'    => false, 'dictOrder' => false,
130            'modificationDateOnPictures' => false,
131            'displayModificationDate' => false,
132            'sortByCreationDate' => false, 'defaultPicture' => null, 'tree' => false,
133            'includeItemsInTOC' => false, 'sidebar' => false,
134        );
135    }
136
137    function render($mode, Doku_Renderer $renderer, $data) {
138        $this->_deactivateTheCacheIfNeeded($renderer);
139
140        if ($data['modificationDateOnPictures']){
141            action_plugin_nspages::logUseLegacySyntax();
142        }
143
144        //Load lang now rather than at handle-time, otherwise it doesn't
145        //behave well with the translation plugin (it seems like we cache strings
146        //even if the lang doesn't match)
147        $this->_denullifyLangOptions($data);
148        $this->_denullifyPictureOptions($data);
149        $printer = $this->_selectPrinter($mode, $renderer, $data);
150
151        if ($data['sidebar']) {
152            if ($data['wantedNS'] !== '') {
153                $printer->printErrorSidebarDoestAcceptNamespace($data['wantedNS']);
154                return TRUE;
155            }
156            if ($mode === "metadata") {
157                // In this case $INFO is null so there is not much we can do,
158                // but anyway in "sidebar" mode we're not really going to generate metadata of the current page
159                // so it rather makes sense to exiting without printing anything.
160                return TRUE;
161            }
162            global $INFO;
163            $data['wantedNS'] = $INFO['namespace'];
164            $data['safe'] = true;
165            $data['wantedDir'] = namespaceFinder::namespaceToDirectory($data['wantedNS']);
166        }
167
168
169        if( ! $this->_isNamespaceUsable($data))
170        {
171            if( ! $data['hidenons']) {
172                $printer->printUnusableNamespace($data['wantedNS']);
173            }
174            return TRUE;
175        }
176
177        $fileHelper = new fileHelper($data, $this->getConf('custom_title_allow_list_metadata'));
178        $pages = $fileHelper->getPages();
179        $subnamespaces = $fileHelper->getSubnamespaces();
180        if ( $this->_shouldPrintPagesAmongNamespaces($data) ){
181            $subnamespaces = array_merge($subnamespaces, $pages);
182        }
183
184        $printer->printBeginning();
185        $this->_print($printer, $data, $subnamespaces, $pages);
186        $printer->printEnd();
187        return TRUE;
188    }
189
190    function _denullifyLangOptions(&$data){
191        if ( is_null($data['textNS']) ){
192            $data['textNS'] = $this->getLang('subcats');
193        }
194
195        if ( is_null($data['textPages']) ){
196            $data['textPages'] = $this->getLang('pagesinthiscat');
197        }
198    }
199
200    function _denullifyPictureOptions(&$data){
201        if ( is_null($data['defaultPicture']) ){
202            $data['defaultPicture'] = $this->getConf('default_picture');
203        }
204    }
205
206    private function _shouldPrintPagesAmongNamespaces($data){
207        return $data['pagesinns'];
208    }
209
210    private function _print($printer, $data, $subnamespaces, $pages){
211        if($data['subns']) {
212            $printer->printTOC($subnamespaces, 'subns', $data['textNS'], $data['hidenosubns']);
213        }
214
215        if(!$this->_shouldPrintPagesAmongNamespaces($data)) {
216
217            if ( $this->_shouldPrintTransition($data) ){
218              $printer->printTransition();
219            }
220
221            if(!$data['nopages']) {
222                $printer->printTOC($pages, 'page', $data['textPages'], $data['hidenopages']);
223            }
224        }
225    }
226
227    private function _shouldPrintTransition($data){
228        return $data['textPages'] === '' && !$data['nopages'] && $data['subns'];
229    }
230
231    private function _isNamespaceUsable($data){
232        global $conf;
233        return @opendir($conf['datadir'] . '/' . $data['wantedDir']) !== false && $data['safe'];
234    }
235
236    private function _selectPrinter($mode, &$renderer, $data){
237        if($data['simpleList']) {
238            return new nspages_printerSimpleList($this, $mode, $renderer, $data);
239        } else if($data['numberedList']){
240            return new nspages_printerSimpleList($this, $mode, $renderer, $data, true);
241        } else if($data['simpleLine']) {
242            return new nspages_printerOneLine($this, $mode, $renderer, $data);
243        } else if ($data['lineBreak']){
244            return new nspages_printerLineBreak($this, $mode, $renderer, $data);
245        } else if ($data['usePictures'] && $mode == 'xhtml') { //This printer doesn't support non html mode yet
246            return new nspages_printerPictures($this, $mode, $renderer, $data);
247        } else if ($data['tree']) {
248            return new nspages_printerTree($this, $mode, $renderer, $data);
249        } else if($mode == 'xhtml') {
250            return new nspages_printerNice($this, $mode, $renderer, $data['nbCol'], $data['anchorName'], $data);
251        }
252        return new nspages_printerSimpleList($this, $mode, $renderer, $data);
253    }
254
255    private function _deactivateTheCacheIfNeeded(&$renderer) {
256        if ($this->getConf('cache') == 1){
257            $renderer->nocache(); //disable cache
258        }
259    }
260}
261