1<?php
2/**
3 * Renderer for XHTML output
4 *
5 * @author Harry Fuecks <hfuecks@gmail.com>
6 * @author Andreas Gohr <andi@splitbrain.org>
7 */
8if (!defined('DOKU_INC')) die('meh.');
9
10
11require_once DOKU_INC . 'inc/parser/xhtml.php';
12
13/**
14 * DokuWiki Plugin nicorender (Renderer Component)
15 *
16 * The Nico XHTML Renderer
17 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
18 * @author  Nicolas GERARD <gerardnico@gmail.com>
19 *
20 * This is a replacement render of the DokuWiki's main renderer
21 * That format the content that's output the tpl_content function.
22 */
23class  renderer_plugin_rplus_renderer extends Doku_Renderer_xhtml
24{
25
26    /**
27     * @var array that hold the position of the parent
28     */
29    protected $nodeParentPosition = [];
30
31    /**
32     * @var array that hold the current position of an header for a level
33     * $headerNum[level]=position
34     */
35    protected $header = [];
36
37    /**
38     * @var array that will contains the whole doc but by section
39     */
40    protected $sections = [];
41
42    /**
43     * @var the section number
44     */
45    protected $sectionNumber = 0;
46
47    /**
48     * @var variable that permits to carry the header text of a previous section
49     */
50    protected $previousSectionTextHeader = '';
51
52
53    /**
54     * @var variable that permits to carry the position of a previous section
55     */
56    protected $previousNodePosition = 0;
57
58    /**
59     * @var variable that permits to carry the position of a previous section
60     */
61    protected $previousNodeLevel = 0;
62
63
64    function getFormat()
65    {
66        return 'xhtml';
67    }
68
69    /*
70     * Function that enable to list the plugin in the options for config:renderer_xhtml
71     * http://www.dokuwiki.org/config:renderer_xhtml
72     * setting in its Configuration Manager.
73     */
74    public function canRender($format)
75    {
76        return ($format == 'xhtml');
77    }
78
79
80    /**
81     * Render a heading
82     *
83     * The rendering of the heading is done through the parent
84     * The function just:
85     *   - save the rendering between each header in the class variable $this->sections
86     * This variblae is used in the function document_end to recreate the whole doc.
87     *   - add the numbering to the header text
88     *
89     * @param string $text the text to display
90     * @param int $level header level
91     * @param int $pos byte position in the original source
92     */
93    function header($text, $level, $pos)
94    {
95
96
97        // We are going from 2 to 3
98        // The parent is 2
99        if ($level > $this->previousNodeLevel) {
100            $nodePosition = 1;
101            // Keep the position of the parent
102            $this->nodeParentPosition[$this->previousNodeLevel] = $this->previousNodePosition;
103        } elseif
104            // We are going from 3 to 2
105            // The parent is 1
106        ($level < $this->previousNodeLevel
107        ) {
108            $nodePosition = $this->nodeParentPosition[$level] + 1;
109        } else {
110            $nodePosition = $this->previousNodePosition + 1;
111        }
112
113        // Grab the doc from the previous section
114        $this->sections[$this->sectionNumber] = array(
115            'level' => $this->previousNodeLevel,
116            'position' => $this->previousNodePosition,
117            'content' => $this->doc,
118            'text' => $this->previousSectionTextHeader);
119
120        // And reset it
121        $this->doc = '';
122        // Set the looping variable
123        $this->sectionNumber = $this->sectionNumber + 1;
124        $this->previousNodeLevel = $level;
125        $this->previousNodePosition = $nodePosition;
126        $this->previousSectionTextHeader = $text;
127
128        $numbering = "";
129        if ($level == 2) {
130            $numbering = $nodePosition;
131        }
132        if ($level == 3) {
133            $numbering = $this->nodeParentPosition[$level - 1] . "." . $nodePosition;
134        }
135        if ($level == 4) {
136            $numbering = $this->nodeParentPosition[$level - 2] . "." . $this->nodeParentPosition[$level - 1] . "." . $nodePosition;
137        }
138        if ($level == 5) {
139            $numbering = $this->nodeParentPosition[$level - 3] . "." . $this->nodeParentPosition[$level - 2] . "." . $this->nodeParentPosition[$level - 1] . "." . $nodePosition;
140        }
141        if ($numbering <> "") {
142            $textWithLocalization = $numbering . " - " . $text;
143        } else {
144            $textWithLocalization = $text;
145        }
146
147        // Rendering is done by the parent
148        parent::header($textWithLocalization, $level, $pos);
149
150        // Add the page detail after the first header
151        if ($level == 1 and $nodePosition == 1) {
152
153            global $ID;
154            $this->doc .= $this->youarehere($ID);
155
156        }
157
158
159    }
160
161
162    function document_end()
163    {
164
165        // TOC init
166        // Dow we need to show the toc ?
167        $showToc = $this->getShowToc();
168        global $TOC;
169        // If the TOC is null (The toc may be initialized by a plugin)
170        if (!is_array($TOC)) {
171            $TOC = $this->toc;
172        }
173
174        // Pump the last doc
175        $this->sections[$this->sectionNumber] = array('level' => $this->previousNodeLevel, 'position' => $this->previousNodePosition, 'content' => $this->doc, 'text' => $this->previousSectionTextHeader);
176
177        // Recreate the doc
178        $this->doc = '';
179        foreach ($this->sections as $sectionNumber => $section) {
180
181            // The content
182            $this->doc .= $section['content'];
183
184            if ($section['level'] == 1 and $section['position'] == 1) {
185
186                if ($showToc) {
187                    global $ACT;
188                    switch ($ACT){
189                        case 'admin':
190                            $this->doc .= tpl_toc($return = true);
191                            break;
192                        default:
193                            global $conf;
194                            if (count($TOC) > $conf['tocminheads']) {
195                                $this->doc .= tpl_toc($return = true);
196                            }
197                            break;
198                    }
199                }
200
201                // Advertisement bar after the content ???
202                //                if ($section['level'] == 2 and
203                //                    $section['position'] == 1 and
204                //                    $ID <> 'adbar12' and
205                //                    $ID <> 'start'
206                //                ) {
207                //
208                //                    // $ID <> 'adbar12' to not come in a recursive call
209                //                    // as tpl_include_call also the renderer process
210                //
211                //                    $this->doc .= tpl_include_page('adbar12', $print = false, $propagate = true);
212                //
213                //                }
214
215            }
216
217
218        }
219
220        parent::document_end();
221
222    }
223
224    /**
225     * Start a table
226     *
227     * @param int $maxcols maximum number of columns
228     * @param int $numrows NOT IMPLEMENTED
229     * @param int $pos byte position in the original source
230     * @param string|string[]  classes - have to be valid, do not pass unfiltered user input
231     */
232    function table_open($maxcols = null, $numrows = null, $pos = null, $classes = NULL)
233    {
234        // initialize the row counter used for classes
235        $this->_counter['row_counter'] = 0;
236        $class = 'table';
237        if ($pos !== null) {
238            $sectionEditStartData = ['target' => 'table'];
239            if (!defined('SEC_EDIT_PATTERN')) {
240                // backwards-compatibility for Frusterick Manners (2017-02-19)
241                $sectionEditStartData = 'table';
242            }
243            $class .= ' ' . $this->startSectionEdit($pos, $sectionEditStartData);
244        }
245        // table-responsive and
246        $bootResponsiveClass = 'table-responsive';
247        $bootTableClass = 'table table-hover table-striped';
248
249        $this->doc .= '<div class="' . $class . ' ' . $bootResponsiveClass . '"><table class="inline ' . $bootTableClass . '">' . DOKU_LF;
250
251    }
252
253
254    /**
255     * Hierarchical breadcrumbs
256     *
257     * This will return the Hierarchical breadcrumbs.
258     *
259     * Config:
260     *    - $conf['youarehere'] must be true
261     *    - add $lang['youarehere'] if $printPrefix is true
262     *
263     * @return string
264     */
265    function youarehere()
266    {
267
268        global $conf;
269        global $lang;
270
271        // check if enabled
272        if (!$conf['youarehere']) return;
273
274        // print intermediate namespace links
275        $htmlOutput = '<ol class="breadcrumb rplus">' . PHP_EOL;
276
277        // Print the home page
278        $htmlOutput .= '<li>' . PHP_EOL;
279        $page = $conf['start'];
280        $pageTitle = tpl_pagetitle($page, true);
281        $htmlOutput .= tpl_link(wl($page), '<span class="nicon_home" aria-hidden="true"></span>', 'title="' . $pageTitle . '"', $return = true);
282        $htmlOutput .= '</li>' . PHP_EOL;
283
284        // Print the parts if there is more than one
285        global $ID;
286        $idParts = explode(':', $ID);
287        $countPart = count($idParts);
288        if ($countPart > 1) {
289
290            // Print the parts without the last one ($count -1)
291            $pagePart = "";
292            for ($i = 0; $i < $countPart - 1; $i++) {
293
294                $pagePart .= $idParts[$i] . ':';
295
296                // We pass the value to the page variable
297                // because the resolve part will change it
298                $page = $pagePart;
299                $exist = null;
300                resolve_pageid(getNS($ID), $page, $exist, "", true);
301
302                $pageTitle = tpl_pagetitle($page, true);
303                $linkContent = $pageTitle;
304                if ($i < $countPart - 1) {
305                    $linkContent = " > " . $linkContent;
306                }
307                $htmlOutput .= '<li>';
308                // html_wikilink because the page has the form pagename: and not pagename:pagename
309                $htmlOutput .= tpl_link(wl($page), $linkContent, 'title="' . $pageTitle . '" class="navlink"', $return = true);
310                $htmlOutput .= '</li>' . PHP_EOL;
311
312            }
313        }
314
315
316        // print current page
317        //    print '<li>';
318        //    tpl_link(wl($page), tpl_pagetitle($page,true), 'title="' . $page . '"');
319        $htmlOutput .= '</li>' . PHP_EOL;
320
321        // close the breadcrumb
322        $htmlOutput .= '</ol>' . PHP_EOL;
323        return $htmlOutput;
324
325    }
326
327    /**
328     * @return bool if the toc need to be shown
329     */
330    private function getShowToc()
331    {
332        // No TOC or bar for an admin page
333        global $ACT;
334        $showToc = null;
335
336        if ($ACT == 'search') {
337
338            $showToc = false;
339
340        }
341
342        if ($ACT == 'admin' and $showToc == null) {
343
344            global $INPUT;
345            $plugin = null;
346            $class = $INPUT->str('page');
347            if (!empty($class)) {
348
349                $pluginlist = plugin_list('admin');
350
351                if (in_array($class, $pluginlist)) {
352                    // attempt to load the plugin
353                    /** @var $plugin DokuWiki_Admin_Plugin */
354                    $plugin = plugin_load('admin', $class);
355                }
356
357                if ($plugin !== null) {
358                    global $TOC;
359                    if (!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
360                    if (!is_array($TOC)) {
361                        $showToc = false;
362                    } else {
363                        $showToc = true;
364                    }
365
366                }
367
368            }
369
370        }
371
372        // Default True
373        if ($showToc == null) {
374            $showToc = true;
375        }
376
377
378        return $showToc;
379
380    }
381
382
383}
384