1<?php
2/**
3 * DokuWiki Plugin DocNavigation (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Gerrit Uitslag <klapinklapin@gmail.com>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12/**
13 * Handles document navigation syntax
14 */
15class syntax_plugin_docnavigation_pagenav extends DokuWiki_Syntax_Plugin {
16
17    /**
18     * Stores data of navigation per page (for preview)
19     *
20     * @var array
21     */
22    public $data = array();
23
24    /**
25     * Syntax Type
26     *
27     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
28     *
29     * @return string
30     */
31    public function getType() {
32        return 'substition';
33    }
34
35    /**
36     * Paragraph Type
37     *
38     * Defines how this syntax is handled regarding paragraphs. This is important
39     * for correct XHTML nesting. Should return one of the following:
40     *
41     * 'normal' - The plugin can be used inside paragraphs
42     * 'block'  - Open paragraphs need to be closed before plugin output
43     * 'stack'  - Special case. Plugin wraps other paragraphs.
44     *
45     * @see Doku_Handler_Block
46     *
47     * @return string
48     */
49    public function getPType() {
50        return 'block';
51    }
52
53    /**
54     * Sort for applying this mode
55     *
56     * @return int
57     */
58    public function getSort() {
59        return 150;
60    }
61
62    /**
63     * @param string $mode
64     */
65    public function connectTo($mode) {
66        $this->Lexer->addSpecialPattern('<-[^\n]*\^[^\n]*\^[^\n]*->', $mode, 'plugin_docnavigation_pagenav');
67        $this->Lexer->addSpecialPattern('<<[^\n]*\^[^\n]*\^[^\n]*>>', $mode, 'plugin_docnavigation_pagenav');
68    }
69
70    /**
71     * Handler to prepare matched data for the rendering process
72     *
73     * Usually you should only need the $match param.
74     *
75     * @param   string       $match   The text matched by the patterns
76     * @param   int          $state   The lexer state for the match
77     * @param   int          $pos     The character position of the matched text
78     * @param   Doku_Handler $handler The Doku_Handler object
79     * @return  bool|array Return an array with all data you want to use in render, false don't add an instruction
80     */
81    public function handle($match, $state, $pos, Doku_Handler $handler) {
82        global $conf, $ID;
83
84        // links are: 0=previous, 1=toc, 2=next
85        $linkstrs = explode("^", substr($match, 2, -2), 3);
86        $links = array();
87        foreach($linkstrs as $index => $link) {
88            // Split title from URL
89            $link = explode('|',$link,2);
90            if ( !isset($link[1]) ) {
91                $link[1] = null;
92            } else if (preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
93                // If the title is an image, convert it to an array containing the image details
94                $link[1] = Doku_Handler_Parse_Media($link[1]);
95            }
96
97            $link[0] = trim($link[0]);
98
99            //look for an existing headpage when toc is empty
100            if($index == 1 && empty($link[0])) {
101                $ns = getNS($ID);
102                if(page_exists($ns.':'.$conf['start'])) {
103                    // start page inside namespace
104                    $link[0] = $ns.':'.$conf['start'];
105                }elseif(page_exists($ns.':'.noNS($ns))) {
106                    // page named like the NS inside the NS
107                    $link[0] = $ns.':'.noNS($ns);
108                }elseif(page_exists($ns)) {
109                    // page like namespace exists
110                    $link[0] = (!getNS($ns) ? ':':'').$ns;
111                }
112            }
113            //store original link with special chars and upper cases
114            $link[2] = $link[0];
115
116            // resolve and clean up the $id
117            resolve_pageid(getNS($ID), $link[0], $exists);
118            @list($link[0]) = explode('#', $link[0], 2);
119
120            //previous or next should not point to itself
121            if($index !== 1 && $link[0] == $ID) {
122                $link[0] = '';
123            }
124
125            $links[] = $link;
126        }
127
128        $data = array(
129            'previous' => $links[0],
130            'toc'      => $links[1],
131            'next'     => $links[2]
132        );
133
134        // store data for preview
135        $this->data[$ID] = $data;
136
137        // return instruction data for renderers
138        return $data;
139    }
140
141    /**
142     * Handles the actual output creation.
143     *
144     * @param string          $mode     output format being rendered
145     * @param Doku_Renderer   $renderer the current renderer object
146     * @param array           $data     data created by handler()
147     * @return  boolean                 rendered correctly? (however, returned value is not used at the moment)
148     */
149    public function render($mode, Doku_Renderer $renderer, $data) {
150        if($mode == 'metadata') {
151            /** @var Doku_Renderer_metadata $renderer */
152            $renderer->meta['docnavigation'] = $data;
153
154            foreach($data as $url) {
155                if($url) {
156                    if($url[1] === null) {
157                        $defaulttitle = $renderer->_simpleTitle($url[2]);
158                        $url[1] = $renderer->_getLinkTitle(null, $defaulttitle, $url[0]);
159                    }
160                    $renderer->internallink($url[0], $url[1]);
161                }
162            }
163            return true;
164        }
165
166        return false;
167    }
168}
169
170// vim:ts=4:sw=4:et:
171